move ffadds to not conflict with fptrans -- makes space for min/max/fmod/remainder ops
[openpower-isa.git] / src / openpower / decoder / power_insn.py
index 5eee84061dbace0953e88c86bcbbe48afa098601..2c28fc9e466df07d88201c29097a28bc20d5b754 100644 (file)
@@ -4,6 +4,7 @@ import dataclasses as _dataclasses
 import enum as _enum
 import functools as _functools
 import os as _os
+import operator as _operator
 import pathlib as _pathlib
 import re as _re
 
@@ -40,13 +41,24 @@ from openpower.decoder.selectable_int import (
 )
 from openpower.decoder.power_fields import (
     Field as _Field,
-    Array as _Array,
     Mapping as _Mapping,
     DecodeFields as _DecodeFields,
 )
 from openpower.decoder.pseudo.pagereader import ISA as _ISA
 
 
+@_functools.total_ordering
+class Verbosity(_enum.Enum):
+    SHORT = _enum.auto()
+    NORMAL = _enum.auto()
+    VERBOSE = _enum.auto()
+
+    def __lt__(self, other):
+        if not isinstance(other, self.__class__):
+            return NotImplemented
+        return (self.value < other.value)
+
+
 def dataclass(cls, record, keymap=None, typemap=None):
     if keymap is None:
         keymap = {}
@@ -347,6 +359,63 @@ class SVP64Record:
 
         return dataclass(cls, record, keymap=cls.__KEYMAP)
 
+    @_functools.lru_cache(maxsize=None)
+    def extra_idx(self, key):
+        extra_idx = (
+            _SVExtra.Idx0,
+            _SVExtra.Idx1,
+            _SVExtra.Idx2,
+            _SVExtra.Idx3,
+        )
+
+        if key not in frozenset({
+                    "in1", "in2", "in3", "cr_in",
+                    "out", "out2", "cr_out",
+                }):
+            raise KeyError(key)
+
+        sel = getattr(self, key)
+        if sel is _CRInSel.BA_BB:
+            return _SVExtra.Idx_1_2
+        reg = _SVExtraReg(sel)
+        if reg is _SVExtraReg.NONE:
+            return _SVExtra.NONE
+
+        extra_map = {
+            _SVExtraRegType.SRC: {},
+            _SVExtraRegType.DST: {},
+        }
+        for index in range(0, 4):
+            for entry in self.extra[index]:
+                extra_map[entry.regtype][entry.reg] = extra_idx[index]
+
+        for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
+            extra = extra_map[regtype].get(reg, _SVExtra.NONE)
+            if extra is not _SVExtra.NONE:
+                return extra
+
+        return _SVExtra.NONE
+
+    extra_idx_in1 = property(_functools.partial(extra_idx, key="in1"))
+    extra_idx_in2 = property(_functools.partial(extra_idx, key="in2"))
+    extra_idx_in3 = property(_functools.partial(extra_idx, key="in3"))
+    extra_idx_out = property(_functools.partial(extra_idx, key="out"))
+    extra_idx_out2 = property(_functools.partial(extra_idx, key="out2"))
+    extra_idx_cr_in = property(_functools.partial(extra_idx, key="cr_in"))
+    extra_idx_cr_out = property(_functools.partial(extra_idx, key="cr_out"))
+
+    @_functools.lru_cache(maxsize=None)
+    def extra_reg(self, key):
+        return _SVExtraReg(getattr(self, key))
+
+    extra_reg_in1 = property(_functools.partial(extra_reg, key="in1"))
+    extra_reg_in2 = property(_functools.partial(extra_reg, key="in2"))
+    extra_reg_in3 = property(_functools.partial(extra_reg, key="in3"))
+    extra_reg_out = property(_functools.partial(extra_reg, key="out"))
+    extra_reg_out2 = property(_functools.partial(extra_reg, key="out2"))
+    extra_reg_cr_in = property(_functools.partial(extra_reg, key="cr_in"))
+    extra_reg_cr_out = property(_functools.partial(extra_reg, key="cr_out"))
+
 
 class BitSel:
     def __init__(self, value=(0, 32)):
@@ -425,7 +494,7 @@ class Fields:
 
         def transform(item):
             (name, bitrange) = item
-            return (name, list(bitrange.values()))
+            return (name, tuple(bitrange.values()))
 
         self.__mapping = dict(map(transform, items))
 
@@ -448,22 +517,122 @@ class Fields:
 class Operand:
     name: str
 
-    def disassemble(self, value, record, verbose=False):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         raise NotImplementedError
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
 class DynamicOperand(Operand):
-    def disassemble(self, value, record, verbose=False):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         span = record.fields[self.name]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
+
+        if verbosity >= Verbosity.VERBOSE:
+            span = map(str, span)
+            yield f"{indent}{self.name}"
+            yield f"{indent}{indent}{int(value):0{value.bits}b}"
+            yield f"{indent}{indent}{', '.join(span)}"
         else:
             yield str(int(value))
 
 
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperandReg(DynamicOperand):
+    def spec(self, insn, record):
+        vector = False
+        span = record.fields[self.name]
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
+
+        if isinstance(insn, SVP64Instruction):
+            extra_idx = self.extra_idx(record=record)
+
+            if record.etype is _SVEtype.EXTRA3:
+                extra = insn.prefix.rm.extra3[extra_idx]
+            elif record.etype is _SVEtype.EXTRA2:
+                extra = insn.prefix.rm.extra2[extra_idx]
+            else:
+                raise ValueError(record.etype)
+
+            if extra != 0:
+                vector = bool(extra[0])
+                span = tuple(map(str, span))
+                extra_span = extra.__class__
+                if record.etype is _SVEtype.EXTRA3:
+                    extra_span = tuple(map(str, extra_span[1, 2]))
+                    extra = extra[1, 2]
+                elif record.etype is _SVEtype.EXTRA2:
+                    extra_span = tuple(map(str, extra_span[1,]))
+                    extra = _SelectableInt(value=extra[1].value, bits=2)
+                    if vector:
+                        extra <<= 1
+                        extra_span = (extra_span + ("{0}",))
+                    else:
+                        extra_span = (("{0}",) + extra_span)
+                else:
+                    raise ValueError(record.etype)
+
+                bits = (len(span) + len(extra_span))
+                value = _SelectableInt(value=value.value, bits=bits)
+                extra = _SelectableInt(value=extra.value, bits=bits)
+                if vector:
+                    value = ((value << 2) | extra)
+                    span = (span + extra_span)
+                else:
+                    value = ((extra << 5) | value)
+                    span = (extra_span + span)
+
+                value = _SelectableInt(value=value, bits=bits)
+
+        else:
+            value = insn[span]
+
+        span = tuple(map(str, span))
+
+        return (vector, value, span)
+
+    @property
+    def extra_reg(self):
+        return _SVExtraReg(self.name)
+
+    def extra_idx(self, record):
+        for key in frozenset({
+                    "in1", "in2", "in3", "cr_in",
+                    "out", "out2", "cr_out",
+                }):
+            extra_reg = record.svp64.extra_reg(key=key)
+            if extra_reg is self.extra_reg:
+                return record.extra_idx(key=key)
+
+        return _SVExtra.NONE
+
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, prefix="", indent=""):
+        (vector, value, span) = self.spec(insn=insn, record=record)
+
+        if verbosity >= Verbosity.VERBOSE:
+            yield f"{indent}{self.name}"
+            yield f"{indent}{indent}{int(value):0{value.bits}b}"
+            yield f"{indent}{indent}{', '.join(span)}"
+            if isinstance(insn, SVP64Instruction):
+                extra_idx = self.extra_idx(record)
+                if record.etype is _SVEtype.NONE:
+                    yield f"{indent}{indent}extra[none]"
+                else:
+                    etype = repr(record.etype).lower()
+                    yield f"{indent}{indent}{etype}{extra_idx!r}"
+                yield f"{indent}type"
+                yield f"{indent}{indent}{'vector' if vector else 'scalar'}"
+        else:
+            vector = "*" if vector else ""
+            yield f"{vector}{prefix}{int(value)}"
+
+
 @_dataclasses.dataclass(eq=True, frozen=True)
 class ImmediateOperand(DynamicOperand):
     pass
@@ -473,81 +642,77 @@ class ImmediateOperand(DynamicOperand):
 class StaticOperand(Operand):
     value: int
 
-    def disassemble(self, value, record, verbose=False):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
         span = record.fields[self.name]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
+
+        if verbosity >= Verbosity.VERBOSE:
+            span = map(str, span)
+            yield f"{indent}{self.name}"
+            yield f"{indent}{indent}{int(value):0{value.bits}b}"
+            yield f"{indent}{indent}{', '.join(span)}"
         else:
             yield str(int(value))
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandTargetAddrLI(DynamicOperand):
-    @property
-    def name(self):
-        return "LI"
-
-    @name.setter
-    def name(self, _):
-        pass
-
-    def disassemble(self, value, record, verbose=False):
-        span = record.fields["LI"]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
-            yield "target_addr = EXTS(LI || 0b00))"
+class DynamicOperandTargetAddr(DynamicOperandReg):
+    def disassemble(self, insn, record, field,
+            verbosity=Verbosity.NORMAL, indent=""):
+        span = record.fields[field]
+        if isinstance(insn, SVP64Instruction):
+            span = tuple(map(lambda bit: (bit + 32), span))
+        value = insn[span]
+
+        if verbosity >= Verbosity.VERBOSE:
+            span = tuple(map(str, span))
+            yield f"{indent}{self.name}"
+            yield f"{indent}{indent}{int(value):0{value.bits}b}00"
+            yield f"{indent}{indent}{', '.join(span + ('{0}', '{0}'))}"
+            yield f"{indent}{indent}target_addr = EXTS({field} || 0b00))"
         else:
             yield hex(int(_selectconcat(value,
                 _SelectableInt(value=0b00, bits=2))))
 
 
-class DynamicOperandTargetAddrBD(DynamicOperand):
-    @property
-    def name(self):
-        return "BD"
+@_dataclasses.dataclass(eq=True, frozen=True)
+class DynamicOperandTargetAddrLI(DynamicOperandTargetAddr):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        return super().disassemble(field="LI",
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
-    @name.setter
-    def name(self, _):
-        pass
 
-    def disassemble(self, value, record, verbose=False):
-        span = record.fields["BD"]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
-            yield "target_addr = EXTS(BD || 0b00))"
-        else:
-            yield hex(int(_selectconcat(value,
-                _SelectableInt(value=0b00, bits=2))))
+class DynamicOperandTargetAddrBD(DynamicOperandTargetAddr):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        return super().disassemble(field="BD",
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandGPR(DynamicOperand):
-    def disassemble(self, value, record, verbose=False):
-        span = record.fields[self.name]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
-        else:
-            yield f"r{str(int(value))}"
+class DynamicOperandGPR(DynamicOperandReg):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        prefix = "" if (verbosity <= Verbosity.SHORT) else "r"
+        yield from super().disassemble(prefix=prefix,
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
 @_dataclasses.dataclass(eq=True, frozen=True)
-class DynamicOperandFPR(DynamicOperand):
-    def disassemble(self, value, record, verbose=False):
-        span = record.fields[self.name]
-        value = value[span]
-        if verbose:
-            yield f"{int(value):0{value.bits}b}"
-            yield repr(span)
-        else:
-            yield f"f{str(int(value))}"
+class DynamicOperandFPR(DynamicOperandReg):
+    def disassemble(self, insn, record,
+            verbosity=Verbosity.NORMAL, indent=""):
+        prefix = "" if (verbosity <= Verbosity.SHORT) else "f"
+        yield from super().disassemble(prefix=prefix,
+            insn=insn, record=record,
+            verbosity=verbosity, indent=indent)
 
 
 class Operands(tuple):
@@ -630,13 +795,6 @@ class Record:
     operands: Operands
     svp64: SVP64Record = None
 
-    __EXTRA = (
-        _SVExtra.Idx0,
-        _SVExtra.Idx1,
-        _SVExtra.Idx2,
-        _SVExtra.Idx3,
-    )
-
     def __lt__(self, other):
         if not isinstance(other, Record):
             return NotImplemented
@@ -690,54 +848,19 @@ class Record:
     def cr_out(self):
         return self.ppc.cr_out
 
-    def sv_extra(self, key):
-        if key not in frozenset({
-                    "in1", "in2", "in3", "cr_in",
-                    "out", "out2", "cr_out",
-                }):
-            raise KeyError(key)
+    ptype = property(lambda self: self.svp64.ptype)
+    etype = property(lambda self: self.svp64.etype)
 
-        sel = getattr(self.svp64, key)
-        if sel is _CRInSel.BA_BB:
-            return _SVExtra.Idx_1_2
-        reg = _SVExtraReg(sel)
-        if reg is _SVExtraReg.NONE:
-            return _SVExtra.NONE
-
-        extra_map = {
-            _SVExtraRegType.SRC: {},
-            _SVExtraRegType.DST: {},
-        }
-        for index in range(0, 4):
-            for entry in self.svp64.extra[index]:
-                extra_map[entry.regtype][entry.reg] = Record.__EXTRA[index]
-
-        for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
-            extra = extra_map[regtype].get(reg, _SVExtra.NONE)
-            if extra is not _SVExtra.NONE:
-                return extra
-
-        return _SVExtra.NONE
+    def extra_idx(self, key):
+        return self.svp64.extra_idx(key)
 
-    sv_in1 = property(_functools.partial(sv_extra, key="in1"))
-    sv_in2 = property(_functools.partial(sv_extra, key="in2"))
-    sv_in3 = property(_functools.partial(sv_extra, key="in3"))
-    sv_out = property(_functools.partial(sv_extra, key="out"))
-    sv_out2 = property(_functools.partial(sv_extra, key="out2"))
-    sv_cr_in = property(_functools.partial(sv_extra, key="cr_in"))
-    sv_cr_out = property(_functools.partial(sv_extra, key="cr_out"))
-
-    @property
-    def sv_ptype(self):
-        if self.svp64 is None:
-            return _SVPtype.NONE
-        return self.svp64.ptype
-
-    @property
-    def sv_etype(self):
-        if self.svp64 is None:
-            return _SVEtype.NONE
-        return self.svp64.etype
+    extra_idx_in1 = property(lambda self: self.svp64.extra_idx_in1)
+    extra_idx_in2 = property(lambda self: self.svp64.extra_idx_in2)
+    extra_idx_in3 = property(lambda self: self.svp64.extra_idx_in3)
+    extra_idx_out = property(lambda self: self.svp64.extra_idx_out)
+    extra_idx_out2 = property(lambda self: self.svp64.extra_idx_out2)
+    extra_idx_cr_in = property(lambda self: self.svp64.extra_idx_cr_in)
+    extra_idx_cr_out = property(lambda self: self.svp64.extra_idx_cr_out)
 
 
 class Instruction(_Mapping):
@@ -770,7 +893,60 @@ class Instruction(_Mapping):
     def __hash__(self):
         return hash(int(self))
 
-    def disassemble(self, db, byteorder="little", verbose=False):
+    def record(self, db):
+        record = db[self]
+        if record is None:
+            raise KeyError(self)
+        return record
+
+    def spec(self, db, prefix):
+        record = self.record(db=db)
+
+        dynamic_operands = tuple(map(_operator.itemgetter(0),
+            self.dynamic_operands(db=db)))
+
+        static_operands = []
+        for (name, value) in self.static_operands(db=db):
+            static_operands.append(f"{name}={value}")
+
+        operands = ""
+        if dynamic_operands:
+            operands += f" {','.join(dynamic_operands)}"
+        if static_operands:
+            operands += f" ({' '.join(static_operands)})"
+
+        return f"{prefix}{record.name}{operands}"
+
+    def dynamic_operands(self, db, verbosity=Verbosity.NORMAL):
+        record = self.record(db=db)
+
+        imm = False
+        imm_name = ""
+        imm_value = ""
+        for operand in record.operands.dynamic:
+            name = operand.name
+            dis = operand.disassemble(insn=self, record=record,
+                verbosity=min(verbosity, Verbosity.NORMAL))
+            value = " ".join(dis)
+            if imm:
+                name = f"{imm_name}({name})"
+                value = f"{imm_value}({value})"
+                imm = False
+            if isinstance(operand, ImmediateOperand):
+                imm_name = name
+                imm_value = value
+                imm = True
+            if not imm:
+                yield (name, value)
+
+    def static_operands(self, db):
+        record = self.record(db=db)
+        for operand in record.operands.static:
+            yield (operand.name, operand.value)
+
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
         raise NotImplementedError
 
 
@@ -790,66 +966,43 @@ class WordInstruction(Instruction):
             bits.append(bit)
         return "".join(map(str, bits))
 
-    def spec(self, record):
-        immediate = ""
-        dynamic_operands = []
-        for operand in record.operands.dynamic:
-            name = operand.name
-            if immediate:
-                name = f"{immediate}({name})"
-                immediate = ""
-            if isinstance(operand, ImmediateOperand):
-                immediate = operand.name
-            if not immediate:
-                dynamic_operands.append(name)
-
-        static_operands = []
-        for operand in record.operands.static:
-            static_operands.append(f"{operand.name}={operand.value}")
-
-        operands = ""
-        if dynamic_operands:
-            operands += f" {','.join(dynamic_operands)}"
-        if static_operands:
-            operands += f" ({' '.join(static_operands)})"
-
-        return f"{record.name}{operands}"
-
-    def opcode(self, record):
+    def opcode(self, db):
+        record = self.record(db=db)
         return f"0x{record.opcode.value:08x}"
 
-    def mask(self, record):
+    def mask(self, db):
+        record = self.record(db=db)
         return f"0x{record.opcode.mask:08x}"
 
-    def disassemble(self, db, byteorder="little", verbose=False):
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
         integer = int(self)
-        blob = integer.to_bytes(length=4, byteorder=byteorder)
-        blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+        if verbosity <= Verbosity.SHORT:
+            blob = ""
+        else:
+            blob = integer.to_bytes(length=4, byteorder=byteorder)
+            blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+            blob += "    "
 
-        record = db[self]
+        record = self.record(db=db)
         if record is None:
-            yield f"{blob}    .long 0x{integer:08x}"
+            yield f"{blob}.long 0x{integer:08x}"
             return
 
-        operands = []
-        for operand in record.operands.dynamic:
-            operand = " ".join(operand.disassemble(value=self,
-                record=record, verbose=False))
-            operands.append(operand)
+        operands = tuple(map(_operator.itemgetter(1),
+            self.dynamic_operands(db=db, verbosity=verbosity)))
         if operands:
-            operands = ",".join(operands)
-            operands = f" {operands}"
+            yield f"{blob}{record.name} {','.join(operands)}"
         else:
-            operands = ""
-
-        yield f"{blob}    {record.name}{operands}"
+            yield f"{blob}{record.name}"
 
-        if verbose:
+        if verbosity >= Verbosity.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
-            spec = self.spec(record=record)
-            opcode = self.opcode(record=record)
-            mask = self.mask(record=record)
+            spec = self.spec(db=db, prefix="")
+            opcode = self.opcode(db=db)
+            mask = self.mask(db=db)
             yield f"{indent}spec"
             yield f"{indent}{indent}{spec}"
             yield f"{indent}binary"
@@ -862,12 +1015,8 @@ class WordInstruction(Instruction):
             yield f"{indent}mask"
             yield f"{indent}{indent}{mask}"
             for operand in record.operands:
-                name = operand.name
-                yield f"{indent}{name}"
-                parts = operand.disassemble(value=self,
-                    record=record, verbose=True)
-                for part in parts:
-                    yield f"{indent}{indent}{part}"
+                yield from operand.disassemble(insn=self, record=record,
+                    verbosity=verbosity, indent=indent)
             yield ""
 
 
@@ -1077,6 +1226,51 @@ class LDSTIdxMode(Mode):
     prrc0: prrc0
 
 
+class ExtraSpec(_Mapping):
+    _: _Field = range(0, 9)
+
+
+class Extra2Spec(ExtraSpec):
+    idx0: _Field = range(0, 2)
+    idx1: _Field = range(2, 4)
+    idx2: _Field = range(4, 6)
+    idx3: _Field = range(6, 8)
+
+    def __getitem__(self, key):
+        return {
+            0: self.idx0,
+            1: self.idx1,
+            2: self.idx2,
+            3: self.idx3,
+            _SVExtra.Idx0: self.idx0,
+            _SVExtra.Idx1: self.idx1,
+            _SVExtra.Idx2: self.idx2,
+            _SVExtra.Idx3: self.idx3,
+        }[key]
+
+    def __setitem__(self, key, value):
+        self[key].assign(value)
+
+
+class Extra3Spec(ExtraSpec):
+    idx0: _Field = range(0, 3)
+    idx1: _Field = range(3, 6)
+    idx2: _Field = range(6, 9)
+
+    def __getitem__(self, key):
+        return {
+            0: self.idx0,
+            1: self.idx1,
+            2: self.idx2,
+            _SVExtra.Idx0: self.idx0,
+            _SVExtra.Idx1: self.idx1,
+            _SVExtra.Idx2: self.idx2,
+        }[key]
+
+    def __setitem__(self, key, value):
+        self[key].assign(value)
+
+
 class RM(_Mapping):
     class Mode(Mode):
         normal: NormalMode
@@ -1089,20 +1283,12 @@ class RM(_Mapping):
     elwidth: _Field = range(4, 6)
     ewsrc: _Field = range(6, 8)
     subvl: _Field = range(8, 10)
-    extra: _Field = range(10, 19)
     mode: Mode.remap(range(19, 24))
-    extra2: _Array[4] = (
-        range(10, 12),
-        range(12, 14),
-        range(14, 16),
-        range(16, 18),
-    )
     smask: _Field = range(16, 19)
-    extra3: _Array[3] = (
-        range(10, 13),
-        range(13, 16),
-        range(16, 19),
-    )
+
+    extra: ExtraSpec.remap(range(10, 19))
+    extra2: Extra2Spec.remap(range(10, 19))
+    extra3: Extra3Spec.remap(range(10, 19))
 
 
 class SVP64Instruction(PrefixedInstruction):
@@ -1121,34 +1307,20 @@ class SVP64Instruction(PrefixedInstruction):
             bits.append(bit)
         return "".join(map(str, bits))
 
-    def spec(self, record):
-        return f"sv.{self.suffix.spec(record=record)}"
-
-    def opcode(self, record):
-        return self.suffix.opcode(record=record)
-
-    def mask(self, record):
-        return self.suffix.mask(record=record)
+    def opcode(self, db):
+        return self.suffix.opcode(db=db)
 
-    def disassemble(self, db, byteorder="little", verbose=False):
-        integer_prefix = int(self.prefix)
-        blob_prefix = integer_prefix.to_bytes(length=4, byteorder=byteorder)
-        blob_prefix = " ".join(map(lambda byte: f"{byte:02x}", blob_prefix))
+    def mask(self, db):
+        return self.suffix.mask(db=db)
 
-        integer_suffix = int(self.suffix)
-        blob_suffix = integer_suffix.to_bytes(length=4, byteorder=byteorder)
-        blob_suffix = " ".join(map(lambda byte: f"{byte:02x}", blob_suffix))
-
-        record = db[self.suffix]
-        if record is None or record.svp64 is None:
-            yield f"{blob_prefix}    .long 0x{int(self.prefix):08x}"
-            yield f"{blob_suffix}    .long 0x{int(self.suffix):08x}"
-            return
+    def mode(self, db):
+        record = self.record(db=db)
 
         Rc = False
         if record.operands["Rc"] is not None:
             Rc = bool(self[record.fields["Rc"]])
 
+        record = self.record(db=db)
         subvl = self.prefix.rm.subvl
         mode = self.prefix.rm.mode
         sel = mode.sel
@@ -1220,18 +1392,76 @@ class SVP64Instruction(PrefixedInstruction):
                 else:
                     mode = mode.prrc0
 
-        if type(mode) is Mode:
-            raise NotImplementedError
+        modes = {
+            NormalMode.simple: "normal: simple",
+            NormalMode.smr: "normal: smr",
+            NormalMode.pmr: "normal: pmr",
+            NormalMode.svmr: "normal: svmr",
+            NormalMode.pu: "normal: pu",
+            NormalMode.ffrc1: "normal: ffrc1",
+            NormalMode.ffrc0: "normal: ffrc0",
+            NormalMode.sat: "normal: sat",
+            NormalMode.satx: "normal: satx",
+            NormalMode.satpu: "normal: satpu",
+            NormalMode.prrc1: "normal: prrc1",
+            NormalMode.prrc0: "normal: prrc0",
+            LDSTImmMode.simple: "ld/st imm: simple",
+            LDSTImmMode.spu: "ld/st imm: spu",
+            LDSTImmMode.ffrc1: "ld/st imm: ffrc1",
+            LDSTImmMode.ffrc0: "ld/st imm: ffrc0",
+            LDSTImmMode.sat: "ld/st imm: sat",
+            LDSTImmMode.prrc1: "ld/st imm: prrc1",
+            LDSTImmMode.prrc0: "ld/st imm: prrc0",
+            LDSTIdxMode.simple: "ld/st idx simple",
+            LDSTIdxMode.stride: "ld/st idx stride",
+            LDSTIdxMode.sat: "ld/st idx sat",
+            LDSTIdxMode.prrc1: "ld/st idx prrc1",
+            LDSTIdxMode.prrc0: "ld/st idx prrc0",
+        }
+        for (cls, desc) in modes.items():
+            if isinstance(mode, cls):
+                return (mode, desc)
+
+        if record.svp64.mode is _SVMode.BRANCH:
+            return (self.prefix.rm.mode, "branch")
 
-        yield f"{blob_prefix}    sv.{record.name}"
+        raise ValueError(self)
+
+    def disassemble(self, db,
+            byteorder="little",
+            verbosity=Verbosity.NORMAL):
+        def blob(integer):
+            if verbosity <= Verbosity.SHORT:
+                return ""
+            else:
+                blob = integer.to_bytes(length=4, byteorder=byteorder)
+                blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
+                return f"{blob}    "
+
+        blob_prefix = blob(int(self.prefix))
+        blob_suffix = blob(int(self.suffix))
+        record = self.record(db=db)
+        if record is None or record.svp64 is None:
+            yield f"{blob_prefix}.long 0x{int(self.prefix):08x}"
+            yield f"{blob_suffix}.long 0x{int(self.suffix):08x}"
+            return
+
+        operands = tuple(map(_operator.itemgetter(1),
+            self.dynamic_operands(db=db, verbosity=verbosity)))
+        if operands:
+            yield f"{blob_prefix}sv.{record.name} {','.join(operands)}"
+        else:
+            yield f"{blob_prefix}{record.name}"
         yield f"{blob_suffix}"
 
-        if verbose:
+        (mode, mode_desc) = self.mode(db=db)
+
+        if verbosity >= Verbosity.VERBOSE:
             indent = (" " * 4)
             binary = self.binary
-            spec = self.spec(record=record)
-            opcode = self.opcode(record=record)
-            mask = self.mask(record=record)
+            spec = self.spec(db=db, prefix="sv.")
+            opcode = self.opcode(db=db)
+            mask = self.mask(db=db)
             yield f"{indent}spec"
             yield f"{indent}{indent}{spec}"
             yield f"{indent}binary"
@@ -1248,12 +1478,11 @@ class SVP64Instruction(PrefixedInstruction):
             yield f"{indent}mask"
             yield f"{indent}{indent}{mask}"
             for operand in record.operands:
-                name = operand.name
-                yield f"{indent}{name}"
-                parts = operand.disassemble(value=self,
-                    record=record, verbose=True)
-                for part in parts:
-                    yield f"{indent}{indent}{part}"
+                yield from operand.disassemble(insn=self, record=record,
+                    verbosity=verbosity, indent=indent)
+
+            yield f"{indent}mode"
+            yield f"{indent}{indent}{mode_desc}"
             yield ""