summaryrefslogtreecommitdiff
path: root/tools/etnaviv/asm_common.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/etnaviv/asm_common.py')
-rw-r--r--tools/etnaviv/asm_common.py229
1 files changed, 229 insertions, 0 deletions
diff --git a/tools/etnaviv/asm_common.py b/tools/etnaviv/asm_common.py
new file mode 100644
index 0000000..de1cff8
--- /dev/null
+++ b/tools/etnaviv/asm_common.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+'''
+Etna shader disassembler/assembler common utils.
+'''
+# Copyright (c) 2012-2013 Wladimir J. van der Laan
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sub license,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the
+# next paragraph) shall be included in all copies or substantial portions
+# of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+from __future__ import print_function, division, unicode_literals
+import argparse,struct
+import sys
+from binascii import b2a_hex
+from collections import namedtuple
+
+from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain
+
+# Register groups
+# t temporary
+# u uniform 0..127
+# v uniform 127..255 (this is rewritten to u in format_src)
+# others are unknown
+RGROUPS = ['t', '?1?', 'u', 'v', '?4?', '?5?', '?6?', '?7?']
+# Addressing modes
+AMODES = ['', 'a.x', 'a.y', 'a.z', 'a.w', '?5?', '?6?', '?7?']
+# components
+COMPS = 'xyzw'
+
+def format_swiz(swiz):
+ swiz = [(swiz >> x)&3 for x in [0,2,4,6]]
+ return ''.join([COMPS[c] for c in swiz])
+def format_comps(comps):
+ return ''.join([COMPS[c] for c in range(4) if ((comps >> c)&1)])
+
+DstOperand = namedtuple('DstOperand', ['use', 'amode', 'reg', 'comps'])
+DstOperandAReg = namedtuple('DstOperandAReg', ['reg', 'comps'])
+SrcOperand = namedtuple('SrcOperand', ['use', 'reg', 'swiz', 'neg', 'abs', 'amode', 'rgroup'])
+TexOperand = namedtuple('TexOperand', ['id', 'amode', 'swiz'])
+AddrOperand = namedtuple('AddrOperand', ['addr'])
+Instruction = namedtuple('Instruction', ['op', 'cond', 'sat', 'tex', 'dst', 'src', 'addr', 'unknowns', 'linenr'])
+
+def disassemble(isa, inst, warnings):
+ '''Parse four 32-bit instruction words into Instruction object'''
+ # Extract bit fields using ISA
+ domain = isa.lookup_domain('VIV_ISA')
+ fields = {}
+ for word in [0,1,2,3]:
+ mask = 0
+ bitset = domain.lookup_address(word*4)[-1][0].type
+ for field in bitset.bitfields:
+ fields[field.name] = field.extract(inst[word])
+ mask |= field.mask
+ if mask != 0xffffffff:
+ warnings.append('isa for word %i incomplete' % word)
+ op = fields['OPCODE']
+
+ if op in [0x0A, 0x0B]: # Move to address register
+ dst = DstOperandAReg(
+ reg = fields['DST_REG'], # reg nr
+ comps = fields['DST_COMPS'] # xyzw
+ )
+ if fields['DST_AMODE'] != 0 or fields['DST_USE'] != 0:
+ warnings.append('use and amode bitfields are nonzero for areg')
+ else:
+ dst = DstOperand(
+ use = fields['DST_USE'], # destination used
+ amode = fields['DST_AMODE'], # addressing mode
+ reg = fields['DST_REG'], # reg nr
+ comps = fields['DST_COMPS'] # xyzw
+ )
+ if not dst.use:
+ if dst.amode != 0 or dst.reg != 0 or dst.comps != 0:
+ warnings.append('dst not used but fields non-zero')
+ dst = None
+
+ tex = TexOperand(
+ id = fields['TEX_ID'], # texture sampler id
+ amode = fields['TEX_AMODE'],
+ swiz = fields['TEX_SWIZ']
+ )
+ if op not in [0x18, 0x19, 0x1A, 0x1B, 0x1C]: # tex op
+ if tex.id != 0 or tex.amode != 0 or tex.swiz != 0:
+ warnings.append('tex not used but fields non-zero')
+ tex = None
+
+ if op in [0x14, 0x16]: # CALL, BRANCH
+ # Address (immediate) operand takes the place of src2
+ addr = AddrOperand(fields['SRC2_IMM'])
+ else:
+ addr = None
+
+ # Determine number of source operands
+ num_src = 3
+ if addr is not None: # src2 is invalid when address operand used
+ num_src = 2
+
+ src = []
+ for idx in xrange(num_src):
+ operand = SrcOperand(
+ use = fields['SRC%i_USE' % idx], reg = fields['SRC%i_REG' % idx],
+ swiz = fields['SRC%i_SWIZ' % idx], neg = fields['SRC%i_NEG' % idx],
+ abs = fields['SRC%i_ABS' % idx], amode = fields['SRC%i_AMODE' % idx],
+ rgroup = fields['SRC%i_RGROUP' % idx]
+ )
+ if not operand.use:
+ if operand.reg != 0 or operand.swiz != 0 or operand.neg != 0 or operand.abs != 0 or operand.amode != 0 or operand.rgroup != 0:
+ warnings.append('src%i not used but fields non-zero' % idx)
+ operand = None
+
+ src.append(operand)
+
+ # Unknown fields -- will warn if these are not 0
+ unknowns = [
+ ('bit_1_21', fields['UNK1_21']), ('bit_2_16', fields['UNK2_16']),
+ ('bit_2_30', fields['UNK2_30']), ('bit_3_24', fields['UNK3_24']),
+ ('bit_3_31', fields['UNK3_31'])
+ ]
+ if addr is None: # bit13 may be set if immediate operand 2
+ unknowns.append(('bit_3_13', fields['UNK3_13']))
+ # verify that all bits in unknown are 0
+ for (name,value) in unknowns:
+ if value != 0:
+ warnings.append('!%s=%i!' % (name,value))
+ return Instruction(op=op,
+ cond=fields['COND'],sat=fields['SAT'],
+ tex=tex,dst=dst,src=src,addr=addr,unknowns=unknowns,linenr=None)
+
+def format_dst(isa, dst):
+ '''Format destination operand'''
+ if dst is not None:
+ # actually, target register group depends on the instruction, but usually it's a temporary...
+ arg = 't%i' % (dst.reg)
+ if dst.amode != 0:
+ arg += '[%s]' % amodes[dst.amode]
+ if dst.comps != 15: # if not all comps selected
+ arg += '.' + format_comps(dst.comps)
+ else:
+ arg = 'void' # unused argument
+
+ return arg
+
+def format_dst_areg(isa, dst):
+ '''Format destination operand'''
+ arg = 'a%i' % (dst.reg)
+ if dst.comps != 15: # if not all comps selected
+ arg += '.' + format_comps(dst.comps)
+
+ return arg
+
+def format_src(isa, src):
+ '''Format source operand'''
+ if src is not None:
+ if src.rgroup == 3: # map vX to uniform u(X+128)
+ rgroup = 2
+ reg = 128 + src.reg
+ else:
+ rgroup = src.rgroup
+ reg = src.reg
+ arg = '%s%i' % (RGROUPS[rgroup], reg)
+ if src.amode != 0:
+ arg += '[%s]' % AMODES[src.amode]
+ if src.swiz != 0xe4: # if not null swizzle
+ arg += '.' + format_swiz(src.swiz)
+ # XXX is the - or the | done first? In a way, -|x| is the only ordering that makes sense.
+ if src.abs:
+ return '|' + arg + '|'
+ if src.neg:
+ return '-' + arg
+ else:
+ arg = 'void' # unused argument
+ return arg
+
+def format_tex(isa, tex):
+ '''Format texture operand'''
+ arg = 'tex%i' % (tex.id)
+ if tex.amode != 0:
+ arg += '[%i]' % amodes[tex.amode]
+ if tex.swiz != 0xe4: # if not null swizzle
+ arg += '.' + format_swiz(tex.swiz)
+
+ return arg
+
+def format_addr(isa, addr):
+ return 'label_%x' % (addr.addr)
+
+def format_instruction(isa, inst):
+ '''
+ Format instruction as text.
+ '''
+ atoms = []
+ args = []
+ atoms.append(isa.types['INST_OPCODE'].describe(inst.op))
+ if inst.cond:
+ atoms.append(isa.types['INST_CONDITION'].describe(inst.cond))
+ if inst.sat:
+ atoms.append(sat)
+ opcode = '.'.join(atoms)
+
+ if isinstance(inst.dst, DstOperandAReg):
+ args.append(format_dst_areg(isa, inst.dst))
+ else:
+ args.append(format_dst(isa, inst.dst))
+
+ if inst.tex is not None:
+ args.append(format_tex(isa, inst.tex))
+
+ for src in inst.src:
+ args.append(format_src(isa, src))
+
+ if inst.addr is not None:
+ args.append(format_addr(isa, inst.addr))
+
+ return opcode+' '+(', '.join(args))
+