power_insn: support verbosity levels
[openpower-isa.git] / src / openpower / sv / trans / svp64.py
index 1c53e00ac76a012554cd45886dd1c0e4a3a50408..7b146fb4ca2a4170abb69f7d4bfcddfe96abc0fa 100644 (file)
@@ -22,19 +22,15 @@ import functools
 import os
 import sys
 from collections import OrderedDict
+import inspect
 
-from openpower.decoder.isa.caller import (SVP64PrefixFields, SV64P_MAJOR_SIZE,
-                                          SV64P_PID_SIZE, SVP64RMFields,
-                                          SVP64RM_EXTRA2_SPEC_SIZE,
-                                          SVP64RM_EXTRA3_SPEC_SIZE,
-                                          SVP64RM_MODE_SIZE, SVP64RM_SMASK_SIZE,
-                                          SVP64RM_MMODE_SIZE, SVP64RM_MASK_SIZE,
-                                          SVP64RM_SUBVL_SIZE, SVP64RM_EWSRC_SIZE,
-                                          SVP64RM_ELWIDTH_SIZE)
 from openpower.decoder.pseudo.pagereader import ISA
 from openpower.decoder.power_svp64 import SVP64RM, get_regtype, decode_extra
 from openpower.decoder.selectable_int import SelectableInt
 from openpower.consts import SVP64MODE
+from openpower.decoder.power_insn import SVP64Instruction
+from openpower.decoder.power_insn import Database
+from openpower.decoder.power_enums import find_wiki_dir
 
 # for debug logging
 from openpower.util import log
@@ -52,6 +48,38 @@ def instruction(*fields):
     return functools.reduce(instruction, fields, 0)
 
 
+CUSTOM_INSNS = {}
+
+
+def _insn(name, **kwargs):
+    return name, kwargs
+
+
+def _custom_insns(*insns):
+    """ a decorator that adds the function to `CUSTOM_INSNS` """
+
+    def decorator(fn):
+        FIELDS_ARG = object()
+        if len(insns) == 0:
+            insns_ = (fn.__name__, {}),
+        else:
+            insns_ = insns
+        for name, kwargs in insns_:
+            if not isinstance(name, str):
+                raise TypeError("instruction name must be a str: {name!r}")
+            if name in CUSTOM_INSNS:
+                raise ValueError(f"duplicate instruction mnemonic: {name!r}")
+            # use getcallargs to check that arguments work:
+            inspect.getcallargs(fn, FIELDS_ARG, **kwargs)
+            CUSTOM_INSNS[name] = functools.partial(fn, **kwargs)
+        return fn
+    return decorator
+
+
+@_custom_insns(
+    _insn("setvl", Rc=0),
+    _insn("setvl.", Rc=1),
+)
 def setvl(fields, Rc):
     """
     setvl is a *32-bit-only* instruction. It controls SVSTATE.
@@ -72,18 +100,22 @@ def setvl(fields, Rc):
     (RT, RA, SVi, vf, vs, ms) = fields
     SVi -= 1
     return instruction(
-        (PO , 0 , 5),
-        (RT , 6 , 10),
-        (RA , 11, 15),
+        (PO, 0, 5),
+        (RT, 6, 10),
+        (RA, 11, 15),
         (SVi, 16, 22),
-        (ms , 23, 23),
-        (vs , 24, 24),
-        (vf , 25, 25),
-        (XO , 26, 30),
-        (Rc , 31, 31),
+        (ms, 23, 23),
+        (vs, 24, 24),
+        (vf, 25, 25),
+        (XO, 26, 30),
+        (Rc, 31, 31),
     )
 
 
+@_custom_insns(
+    _insn("svstep", Rc=0),
+    _insn("svstep.", Rc=1),
+)
 def svstep(fields, Rc):
     """
     svstep is a 32-bit instruction. It updates SVSTATE.
@@ -99,52 +131,102 @@ def svstep(fields, Rc):
     """
     PO = 22
     XO = 0b10011
-    #(RT, RA, SVi, ms, vs, vf) = fields
     (RT, SVi, vf) = fields
     SVi -= 1
     return instruction(
-        (PO , 0 , 5),
-        (RT , 6 , 10),
-        (0 , 11, 15),
+        (PO, 0, 5),
+        (RT, 6, 10),
+        (0, 11, 15),
         (SVi, 16, 22),
-        (0 , 23, 23),
-        (0 , 24, 24),
-        (vf , 25, 25),
-        (XO , 26, 30),
-        (Rc , 31, 31),
+        (0, 23, 23),
+        (0, 24, 24),
+        (vf, 25, 25),
+        (XO, 26, 30),
+        (Rc, 31, 31),
     )
 
 
+@_custom_insns()
 def svshape(fields):
     """
     svshape is a *32-bit-only* instruction. It updates SVSHAPE and SVSTATE.
     It is *not* a 64-bit-prefixed Vector instruction (no sv.svshape, yet),
     it is a Vector *control* instruction.
 
-    * svshape SVxd,SVyd,SVzd,SVRM,vf
+    https://libre-soc.org/openpower/sv/remap/#svshape
+
+    * svshape SVxd,SVyd,SVzd,SVrm,vf
 
     # 1.6.33 SVM-FORM from fields.txt
     # |0     |6        |11      |16    |21    |25 |26    |31  |
-    # | PO   |  SVxd   |   SVyd | SVzd | SVRM |vf |   XO      |
+    # | PO   |  SVxd   |   SVyd | SVzd | SVrm |vf |   XO      |
+
+    note that SVrm is not permitted to be 0b0111, 0b1000 or 0b1001.
+    0b0111 is reserved and 0b100- is for svshape2
 
     """
     PO = 22
     XO = 0b011001
-    (SVxd, SVyd, SVzd, SVRM, vf) = fields
+    (SVxd, SVyd, SVzd, SVrm, vf) = fields
     SVxd -= 1
     SVyd -= 1
     SVzd -= 1
+
+    # check SVrm for reserved (and svshape2) values
+    assert SVrm not in [0b1000, 0b1001], \
+            "svshape reserved SVrm value %s" % bin(SVrm)
+
     return instruction(
-        (PO  , 0 , 5),
-        (SVxd, 6 , 10),
+        (PO, 0, 5),
+        (SVxd, 6, 10),
         (SVyd, 11, 15),
         (SVzd, 16, 20),
-        (SVRM, 21, 24),
-        (vf  , 25, 25),
-        (XO  , 26, 31),
+        (SVrm, 21, 24),
+        (vf, 25, 25),
+        (XO, 26, 31),
+    )
+
+
+@_custom_insns()
+def svshape2(fields):
+    """
+    svshape2 is a *32-bit-only* instruction. It updates SVSHAPE and SVSTATE.
+    It is *not* a 64-bit-prefixed Vector instruction (no sv.svshape2, yet),
+    it is a Vector *control* instruction, and is a sort-of hybrid of
+    svshape and svindex, with the key important feature being the "offset".
+
+    https://libre-soc.org/openpower/sv/remap/discussion
+
+    * svshape2 SVo,SVM2yx,rmm,SVd,sk,mm
+
+    # 1.6.35.1 SVM2-FORM from fields.txt
+    # |0     |6     |10   |11      |16    |21 |24|25 |26    |31  |
+    # | PO   | SVo  |SVMyx|   rmm  | SVd  |XO |mm|sk |   XO      |
+
+    note that this fits into the space of svshape and that XO is
+    split across 2 areas.
+
+    """
+    PO = 22
+    XO = 0b011001
+    XO2 = 0b100 # not really XO2 but hey
+    (offs, yx, rmm, SVd, sk, mm) = fields
+    SVd -= 1 # offset by one
+
+    return instruction(
+        (PO, 0, 5),
+        (offs, 6, 9),  # offset (the whole point of adding svshape2)
+        (yx, 10, 10),  # like svindex
+        (rmm, 11, 15), # ditto svindex
+        (SVd, 16, 20), # ditto svindex
+        (XO2, 21, 23), # actually XO split across 2 places...
+        (mm, 24, 24),  # ditto svindex
+        (sk, 25, 25),  # ditto svindex
+        (XO, 26, 31),
     )
 
 
+@_custom_insns()
 def svindex(fields):
     """
     svindex is a *32-bit-only* instruction. It is a convenience
@@ -155,25 +237,27 @@ def svindex(fields):
 
     1.6.28 SVI-FORM
       |0     |6    |11    |16   |21 |23|24|25|26    31|
-      | PO   |  RS |rmm   | SVd |ew |yx|mm|sk|   XO   |
+      | PO   |  SVG|rmm   | SVd |ew |yx|mm|sk|   XO   |
     """
     # note that the dimension field one subtracted
     PO = 22
     XO = 0b101001
-    (RS, rmm, SVd, ew, yx, mm, sk) = fields
+    (SVG, rmm, SVd, ew, yx, mm, sk) = fields
+    SVd -= 1
     return instruction(
-        (PO , 0 , 5),
-        (RS , 6 , 10),
-        (rmm, 11 , 15),
-        (SVd, 16 , 20),
-        (ew , 21 , 22),
-        (yx , 23 , 23),
-        (mm , 24 , 24),
-        (sk , 25 , 25),
-        (XO , 26 , 31),
+        (PO, 0, 5),
+        (SVG, 6, 10),
+        (rmm, 11, 15),
+        (SVd, 16, 20),
+        (ew, 21, 22),
+        (yx, 23, 23),
+        (mm, 24, 24),
+        (sk, 25, 25),
+        (XO, 26, 31),
     )
 
 
+@_custom_insns()
 def svremap(fields):
     """
     this is a *32-bit-only* instruction. It updates the SVSHAPE SPR
@@ -191,16 +275,16 @@ def svremap(fields):
     XO = 0b111001
     (SVme, mi0, mi1, mi2, mo0, mo1, pst) = fields
     return instruction(
-        (PO  , 0 , 5),
-        (SVme, 6 , 10),
-        (mi0 , 11, 12),
-        (mi1 , 13, 14),
-        (mi2 , 15, 16),
-        (mo0 , 17, 18),
-        (mo1 , 19, 20),
-        (pst , 21, 21),
-        (0   , 22, 22),
-        (XO  , 26, 31),
+        (PO, 0, 5),
+        (SVme, 6, 10),
+        (mi0, 11, 12),
+        (mi1, 13, 14),
+        (mi2, 15, 16),
+        (mo0, 17, 18),
+        (mo1, 19, 20),
+        (pst, 21, 21),
+        (0, 22, 25),
+        (XO, 26, 31),
     )
 
 
@@ -209,6 +293,7 @@ def svremap(fields):
 # them (that's being fixed!)
 # they can - if implementations then choose - be Vectorised
 # because they are general-purpose scalar instructions
+@_custom_insns()
 def bmask(fields):
     """
     1.6.2.2 BM2-FORM
@@ -219,54 +304,66 @@ def bmask(fields):
     XO = 0b010001
     (RT, RA, RB, bm, L) = fields
     return instruction(
-        (PO, 0 , 5),
-        (RT, 6 , 10),
+        (PO, 0, 5),
+        (RT, 6, 10),
         (RA, 11, 15),
         (RB, 16, 20),
         (bm, 21, 25),
-        (L , 26, 26),
+        (L, 26, 26),
         (XO, 27, 31),
     )
 
 
+@_custom_insns(
+    _insn("fsins", Rc=0),
+    _insn("fsins.", Rc=1),
+)
 def fsins(fields, Rc):
     # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
     # however we are out of space with opcode 22
     # 1.6.7 X-FORM
     # |0     |6 |7|8|9  |10  |11|12|13  |15|16|17     |20|21    |31  |
-    # | PO   |   FRT         |   FRA       |   FRB       |   XO |  / |
+    # | PO   |   FRT         |     ///     |   FRB       |   XO |Rc  |
     PO = 59
-    XO = 0b1000001110
+    XO = 0b1001001101
     (FRT, FRB) = fields
     return instruction(
-        (PO , 0 , 5),
-        (FRT, 6 , 10),
-        (0  , 11, 15),
+        (PO, 0, 5),
+        (FRT, 6, 10),
+        (0, 11, 15),
         (FRB, 16, 20),
-        (XO , 21, 30),
-        (Rc , 31, 31),
+        (XO, 21, 30),
+        (Rc, 31, 31),
     )
 
 
+@_custom_insns(
+    _insn("fcoss", Rc=0),
+    _insn("fcoss.", Rc=1),
+)
 def fcoss(fields, Rc):
     # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
     # however we are out of space with opcode 22
     # 1.6.7 X-FORM
     # |0     |6 |7|8|9  |10  |11|12|13  |15|16|17     |20|21    |31  |
-    # | PO   |   FRT         |   FRA       |   FRB       |   XO |  / |
+    # | PO   |   FRT         |     ///     |   FRB       |   XO |Rc  |
     PO = 59
-    XO = 0b1000101110
+    XO = 0b1001101100
     (FRT, FRB) = fields
     return instruction(
-        (PO , 0 , 5),
-        (FRT, 6 , 10),
-        (0  , 11, 15),
+        (PO, 0, 5),
+        (FRT, 6, 10),
+        (0, 11, 15),
         (FRB, 16, 20),
-        (XO , 21, 30),
-        (Rc , 31, 31),
+        (XO, 21, 30),
+        (Rc, 31, 31),
     )
 
 
+@_custom_insns(
+    _insn("ternlogi", Rc=0),
+    _insn("ternlogi.", Rc=1),
+)
 def ternlogi(fields, Rc):
     # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
     # however we are out of space with opcode 22
@@ -277,31 +374,41 @@ def ternlogi(fields, Rc):
     XO = 0
     (RT, RA, RB, TLI) = fields
     return instruction(
-        (PO , 0 , 5),
-        (RT , 6 , 10),
-        (RA , 11, 15),
-        (RB , 16, 20),
+        (PO, 0, 5),
+        (RT, 6, 10),
+        (RA, 11, 15),
+        (RB, 16, 20),
         (TLI, 21, 28),
-        (XO , 29, 30),
-        (Rc , 31, 31),
+        (XO, 29, 30),
+        (Rc, 31, 31),
     )
 
 
-def grev(fields, Rc, imm, wide):
+@_custom_insns(
+    _insn("grev", Rc=0, imm=0, word=0),
+    _insn("grevw", Rc=0, imm=0, word=1),
+    _insn("grevi", Rc=0, imm=1, word=0),
+    _insn("grevwi", Rc=0, imm=1, word=1),
+    _insn("grev.", Rc=1, imm=0, word=0),
+    _insn("grevw.", Rc=1, imm=0, word=1),
+    _insn("grevi.", Rc=1, imm=1, word=0),
+    _insn("grevwi.", Rc=1, imm=1, word=1),
+)
+def grev(fields, Rc, imm, word):
     # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
     # however we are out of space with opcode 22
-    PO = 5
+    insn = PO = 5
     # _ matches fields in table at:
-    # https://libre-soc.org/openPOwer/sv/bitmanip/
+    # https://libre-soc.org/openpower/sv/bitmanip/
     XO = 0b1_0010_110
-    if wide:
+    if word:
         XO |= 0b100_000
     if imm:
         XO |= 0b1000_000
     (RT, RA, XBI) = fields
     insn = (insn << 5) | RT
     insn = (insn << 5) | RA
-    if imm and not wide:
+    if imm and not word:
         assert 0 <= XBI < 64
         insn = (insn << 6) | XBI
         insn = (insn << 9) | XO
@@ -313,6 +420,28 @@ def grev(fields, Rc, imm, wide):
     return insn
 
 
+@_custom_insns(
+    _insn("maxs", XO=0b0111001110, Rc=0),
+    _insn("maxs.", XO=0b0111001110, Rc=1),
+    _insn("maxu", XO=0b0011001110, Rc=0),
+    _insn("maxu.", XO=0b0011001110, Rc=1),
+    _insn("minu", XO=0b0001001110, Rc=0),
+    _insn("minu.", XO=0b0001001110, Rc=1),
+    _insn("mins", XO=0b0101001110, Rc=0),
+    _insn("mins.", XO=0b0101001110, Rc=1),
+    _insn("absdu", XO=0b1011110110, Rc=0),
+    _insn("absdu.", XO=0b1011110110, Rc=1),
+    _insn("absds", XO=0b1001110110, Rc=0),
+    _insn("absds.", XO=0b1001110110, Rc=1),
+    _insn("avgadd", XO=0b1101001110, Rc=0),
+    _insn("avgadd.", XO=0b1101001110, Rc=1),
+    _insn("absdacu", XO=0b1111110110, Rc=0),
+    _insn("absdacu.", XO=0b1111110110, Rc=1),
+    _insn("absdacs", XO=0b0111110110, Rc=0),
+    _insn("absdacs.", XO=0b0111110110, Rc=1),
+    _insn("cprop", XO=0b0110001110, Rc=0),
+    _insn("cprop.", XO=0b0110001110, Rc=1),
+)
 def av(fields, XO, Rc):
     # 1.6.7 X-FORM
     #   |0     |6 |7|8|9  |10  |11|12|13  |15|16|17     |20|21    |31  |
@@ -320,8 +449,8 @@ def av(fields, XO, Rc):
     PO = 22
     (RT, RA, RB) = fields
     return instruction(
-        (PO, 0 , 5),
-        (RT, 6 , 10),
+        (PO, 0, 5),
+        (RT, 6, 10),
         (RA, 11, 15),
         (RB, 16, 20),
         (XO, 21, 30),
@@ -329,46 +458,50 @@ def av(fields, XO, Rc):
     )
 
 
-CUSTOM_INSNS = {}
-for (name, hook) in (
-            ("setvl", setvl),
-            ("svstep", svstep),
-            ("fsins", fsins),
-            ("fcoss", fcoss),
-            ("ternlogi", ternlogi),
-        ):
-    CUSTOM_INSNS[name] = functools.partial(hook, Rc=False)
-    CUSTOM_INSNS[f"{name}."] = functools.partial(hook, Rc=True)
-CUSTOM_INSNS["bmask"] = bmask
-CUSTOM_INSNS["svshape"] = svshape
-CUSTOM_INSNS["svindex"] = svindex
-CUSTOM_INSNS["svremap"] = svremap
-
-for (name, imm, wide) in (
-            ("grev", False, False),
-            ("grevi", True, False),
-            ("grevw", False, True),
-            ("grevwi", True, True),
-        ):
-    CUSTOM_INSNS[name] = functools.partial(grev,
-        imm=("i" in name), wide=("w" in name), Rc=False)
-    CUSTOM_INSNS[f"{name}."] = functools.partial(grev,
-        imm=("i" in name), wide=("w" in name), Rc=True)
-
-for (name, XO) in (
-            ("maxs"   , 0b0111001110),
-            ("maxu"   , 0b0011001110),
-            ("minu"   , 0b0001001110),
-            ("mins"   , 0b0101001110),
-            ("absdu"  , 0b1011110110),
-            ("absds"  , 0b1001110110),
-            ("avgadd" , 0b1101001110),
-            ("absdacu", 0b1111110110),
-            ("absdacs", 0b0111110110),
-            ("cprop"  , 0b0110001110),
-        ):
-    CUSTOM_INSNS[name] = functools.partial(av, XO=XO, Rc=False)
-    CUSTOM_INSNS[f"{name}."] = functools.partial(av, XO=XO, Rc=True)
+@_custom_insns()
+def fmvis(fields):
+    # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
+    # V3.0B 1.6.6 DX-FORM
+    # |0     |6 |7|8|9  |10  |11|12|13  |15|16|17     |26|27    |31  |
+    # | PO   |   FRS         |     d1      |      d0  |      XO |d2  |
+    PO = 22
+    XO = 0b00011
+    (FRS, imm) = fields
+    # first split imm into d1, d0 and d2. sigh
+    d2 = (imm & 1)  # LSB (0)
+    d1 = (imm >> 1) & 0b11111  # bits 1-5
+    d0 = (imm >> 6)  # MSBs 6-15
+    return instruction(
+        (PO, 0, 5),
+        (FRS, 6, 10),
+        (d1,  11, 15),
+        (d0,  16, 25),
+        (XO, 26, 30),
+        (d2, 31, 31),
+    )
+
+
+@_custom_insns()
+def fishmv(fields):
+    # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
+    # V3.0B 1.6.6 DX-FORM
+    # |0     |6 |7|8|9  |10  |11|12|13  |15|16|17     |26|27   |31  |
+    # | PO   |   FRS         |     d1      |      d0  |     XO |d2  |
+    PO = 22
+    XO = 0b01011
+    (FRS, imm) = fields
+    # first split imm into d1, d0 and d2. sigh
+    d2 = (imm & 1)  # LSB (0)
+    d1 = (imm >> 1) & 0b11111  # bits 1-5
+    d0 = (imm >> 6)  # MSBs 6-15
+    return instruction(
+        (PO, 0, 5),
+        (FRS, 6, 10),
+        (d1,  11, 15),
+        (d0,  16, 25),
+        (XO, 26, 30),
+        (d2, 31, 31),
+    )
 
 
 # decode GPR into sv extra
@@ -454,17 +587,30 @@ def decode_bo(encoding):
         "encoding %s for BO Mode not recognised" % encoding
     return pmap[encoding]
 
-# partial-decode fail-first mode
-
 
+# partial-decode fail-first mode
 def decode_ffirst(encoding):
     if encoding in ['RC1', '~RC1']:
         return encoding
     return decode_bo(encoding)
 
 
-def decode_reg(field):
+def decode_reg(field, macros=None):
+    if macros is None:
+        macros = {}
     # decode the field number. "5.v" or "3.s" or "9"
+    # and now also "*0", and "*%0".  note: *NOT* to add "*%rNNN" etc.
+    # https://bugs.libre-soc.org/show_bug.cgi?id=884#c0
+    if field.startswith(("*%", "*")):
+        if field.startswith("*%"):
+            field = field[2:]
+        else:
+            field = field[1:]
+        while field in macros:
+            field = macros[field]
+        return int(field), "vector"  # actual register number
+
+    # try old convention (to be retired)
     field = field.split(".")
     regmode = 'scalar'  # default
     if len(field) == 2:
@@ -484,7 +630,7 @@ def decode_imm(field):
         return None, field
 
 
-def crf_extra(etype, regmode, field, extras):
+def crf_extra(etype, rname, extra_idx, regmode, field, extras):
     """takes a CR Field number (CR0-CR127), splits into EXTRA2/3 and v3.0
     the scalar/vector mode (crNN.v or crNN.s) changes both the format
     of the EXTRA2/3 encoding as well as what range of registers is possible.
@@ -504,7 +650,7 @@ def crf_extra(etype, regmode, field, extras):
                 (rname, str(extras[extra_idx]))
             # all good: encode as scalar
             sv_extra = sv_extra & 0b01
-        else: # vector
+        else:  # vector
             # range is CR0-CR127 in increments of 16
             assert sv_extra & 0b111 == 0, \
                 "vector CR %s cannot fit into EXTRA2 %s" % \
@@ -520,7 +666,7 @@ def crf_extra(etype, regmode, field, extras):
                 (rname, str(extras[extra_idx]))
             # all good: encode as scalar
             sv_extra = sv_extra & 0b11
-        else: # vector
+        else:  # vector
             # range is CR0-CR127 in increments of 8
             assert sv_extra & 0b11 == 0, \
                 "vector CR %s cannot fit into EXTRA3 %s" % \
@@ -530,6 +676,17 @@ def crf_extra(etype, regmode, field, extras):
     return sv_extra, field
 
 
+def to_number(field):
+    if field.startswith("0x"):
+        return eval(field)
+    if field.startswith("0b"):
+        return eval(field)
+    return int(field)
+
+
+db = Database(find_wiki_dir())
+
+
 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
 class SVP64Asm:
     def __init__(self, lst, bigendian=False, macros=None):
@@ -551,8 +708,9 @@ class SVP64Asm:
         macros.update(self.macros)
         isa = self.isa
         svp64 = self.svp64
+        insn_no_comments = insn.partition('#')[0]
         # find first space, to get opcode
-        ls = insn.split(' ')
+        ls = insn_no_comments.split(' ')
         opcode = ls[0]
         # now find opcode fields
         fields = ''.join(ls[1:]).split(',')
@@ -567,10 +725,10 @@ class SVP64Asm:
         # identify if it is a special instruction
         custom_insn_hook = CUSTOM_INSNS.get(opcode)
         if custom_insn_hook is not None:
-            fields = tuple(map(int, fields))
-            insn = custom_insn_hook(fields)
-            log(opcode, bin(insn))
-            yield ".long 0x%x" % insn
+            fields = tuple(map(to_number, fields))
+            insn_num = custom_insn_hook(fields)
+            log(opcode, bin(insn_num))
+            yield ".long 0x%X # %s" % (insn_num, insn)
             return
 
         # identify if is a svp64 mnemonic
@@ -581,62 +739,33 @@ class SVP64Asm:
 
         # start working on decoding the svp64 op: sv.basev30Bop/vec2/mode
         opmodes = opcode.split("/")  # split at "/"
-        v30b_op = opmodes.pop(0)    # first is the v3.0B
+        v30b_op_orig = opmodes.pop(0)    # first is the v3.0B
         # check instruction ends with dot
-        rc_mode = v30b_op.endswith('.')
+        rc_mode = v30b_op_orig.endswith('.')
         if rc_mode:
-            v30b_op = v30b_op[:-1]
-
-        # sigh again, have to recognised LD/ST bit-reverse instructions
-        # this has to be "processed" to fit into a v3.0B without the "sh"
-        # e.g. ldsh is actually ld
-        ldst_shift = v30b_op.startswith("l") and v30b_op.endswith("sh")
+            v30b_op = v30b_op_orig[:-1]
+        else:
+            v30b_op = v30b_op_orig
 
-        if v30b_op not in isa.instr:
+        # look up the 32-bit op (original, with "." if it has it)
+        if v30b_op_orig in isa.instr:
+            isa_instr = isa.instr[v30b_op_orig]
+        else:
             raise Exception("opcode %s of '%s' not supported" %
-                            (v30b_op, insn))
-
-        if ldst_shift:
-            # okaay we need to process the fields and make this:
-            #     ldsh RT, SVD(RA), RC  - 11 bits for SVD, 5 for RC
-            # into this:
-            #     ld RT, D(RA)          - 16 bits
-            # likewise same for SVDS (9 bits for SVDS, 5 for RC, 14 bits for DS)
-            form = isa.instr[v30b_op].form  # get form (SVD-Form, SVDS-Form)
-
-            newfields = []
-            for field in fields:
-                # identify if this is a ld/st immediate(reg) thing
-                ldst_imm = "(" in field and field[-1] == ')'
-                if ldst_imm:
-                    newfields.append(field[:-1].split("("))
-                else:
-                    newfields.append(field)
-
-            immed, RA = newfields[1]
-            immed = int(immed)
-            RC = int(newfields.pop(2))  # better be an integer number!
-            if form == 'SVD':  # 16 bit: immed 11 bits, RC shift up 11
-                immed = (immed & 0b11111111111) | (RC << 11)
-                if immed & (1 << 15):  # should be negative
-                    immed -= 1 << 16
-            if form == 'SVDS':  # 14 bit: immed 9 bits, RC shift up 9
-                immed = (immed & 0b111111111) | (RC << 9)
-                if immed & (1 << 13):  # should be negative
-                    immed -= 1 << 14
-            newfields[1] = "%d(%s)" % (immed, RA)
-            fields = newfields
-
-            # and strip off "sh" from end, and add "sh" to opmodes, instead
-            v30b_op = v30b_op[:-2]
-            opmodes.append("sh")
-            log("rewritten", v30b_op, opmodes, fields)
-
-        if v30b_op not in svp64.instrs:
-            raise Exception("opcode %s of '%s' not an svp64 instruction" %
-                            (v30b_op, insn))
-        v30b_regs = isa.instr[v30b_op].regs[0]  # get regs info "RT, RA, RB"
-        rm = svp64.instrs[v30b_op]             # one row of the svp64 RM CSV
+                            (v30b_op_orig, insn))
+
+        # look up the svp64 op, first the original (with "." if it has it)
+        if v30b_op_orig in svp64.instrs:
+            rm = svp64.instrs[v30b_op_orig]  # one row of the svp64 RM CSV
+        # then without the "." (if there was one)
+        elif v30b_op in svp64.instrs:
+            rm = svp64.instrs[v30b_op]  # one row of the svp64 RM CSV
+        else:
+            raise Exception(f"opcode {v30b_op_orig!r} of "
+                            f"{insn!r} not an svp64 instruction")
+
+        # get regs info e.g. "RT,RA,RB"
+        v30b_regs = isa_instr.regs[0]
         log("v3.0B op", v30b_op, "Rc=1" if rc_mode else '')
         log("v3.0B regs", opcode, v30b_regs)
         log("RM", rm)
@@ -708,7 +837,7 @@ class SVP64Asm:
             if ldst_imm:
                 immed, field = field[:-1].split("(")
 
-            field, regmode = decode_reg(field)
+            field, regmode = decode_reg(field, macros=macros)
             log("    ", extra_idx, rname, rtype,
                 regmode, iname, field, end=" ")
 
@@ -754,17 +883,19 @@ class SVP64Asm:
             # encode SV-CR 3-bit field into extra, v3.0field.
             # 3-bit is for things like BF and BFA
             elif rtype == 'CR_3bit':
-                sv_extra, field =  crf_extra(etype, regmode, field, extras)
+                sv_extra, field = crf_extra(etype, rname, extra_idx,
+                                            regmode, field, extras)
 
             # encode SV-CR 5-bit field into extra, v3.0field
             # 5-bit is for things like BA BB BC BT etc.
             # *sigh* this is the same as 3-bit except the 2 LSBs of the
             # 5-bit field are passed through unaltered.
             elif rtype == 'CR_5bit':
-                cr_subfield = field & 0b11 # record bottom 2 bits for later
+                cr_subfield = field & 0b11  # record bottom 2 bits for later
                 field = field >> 2         # strip bottom 2 bits
                 # use the exact same 3-bit function for the top 3 bits
-                sv_extra, field =  crf_extra(etype, regmode, field, extras)
+                sv_extra, field = crf_extra(etype, rname, extra_idx,
+                                            regmode, field, extras)
                 # reconstruct the actual 5-bit CR field (preserving the
                 # bottom 2 bits, unaltered)
                 field = (field << 2) | cr_subfield
@@ -790,8 +921,10 @@ class SVP64Asm:
         log("new v3.0B fields", v30b_op, v30b_newfields)
         log("extras", extras)
 
-        # rright.  now we have all the info. start creating SVP64 RM
-        svp64_rm = SVP64RMFields()
+        # rright. now we have all the info. start creating SVP64 instruction.
+        svp64_insn = SVP64Instruction.pair(prefix=0, suffix=0)
+        svp64_prefix = svp64_insn.prefix
+        svp64_rm = svp64_insn.prefix.rm
 
         # begin with EXTRA fields
         for idx, sv_extra in extras.items():
@@ -802,11 +935,9 @@ class SVP64Asm:
                 continue
             srcdest, idx, duplicate = idx
             if etype == 'EXTRA2':
-                svp64_rm.extra2[idx].eq(
-                    SelectableInt(sv_extra, SVP64RM_EXTRA2_SPEC_SIZE))
+                svp64_rm.extra2[idx] = sv_extra
             else:
-                svp64_rm.extra3[idx].eq(
-                    SelectableInt(sv_extra, SVP64RM_EXTRA3_SPEC_SIZE))
+                svp64_rm.extra3[idx] = sv_extra
 
         # identify if the op is a LD/ST. the "blegh" way. copied
         # from power_enums.  TODO, split the list _insns down.
@@ -895,9 +1026,6 @@ class SVP64Asm:
                 smmode, smask = decode_predicate(encmode[3:])
                 mmode = smmode
                 has_smask = True
-            # shifted LD/ST
-            elif encmode.startswith("sh"):
-                ldst_shift = True
             # vec2/3/4
             elif encmode.startswith("vec"):
                 subvl = decode_subvl(encmode[3:])
@@ -1022,11 +1150,6 @@ class SVP64Asm:
             assert has_pmask or mask_m_specified, \
                 "dest zeroing requires a dest predicate"
 
-        # check LDST shifted, only available in "normal" mode
-        if is_ldst and ldst_shift:
-            assert sv_mode is None, \
-                "LD shift cannot have modes (%s) applied" % sv_mode
-
         # okaaay, so there are 4 different modes, here, which will be
         # partly-merged-in: is_ldst is merged in with "normal", but
         # is_bc is so different it's done separately.  likewise is_cr
@@ -1036,15 +1159,17 @@ class SVP64Asm:
         """
             | 0-1 |  2  |  3   4  |  description              |
             | --- | --- |---------|-------------------------- |
-            | 00  |   0 |  dz  sz | normal mode                      |
+            | 00  |   0 |  dz  sz | simple mode                      |
             | 00  |   1 | 0  RG   | scalar reduce mode (mapreduce), SUBVL=1 |
-            | 00  |   1 | 1  /    | parallel reduce mode (mapreduce), SUBVL=1 |
-            | 00  |   1 | SVM RG  | subvector reduce mode, SUBVL>1   |
+            | 00  |   1 | SVM 0   | subvector reduce mode, SUBVL>1   |
+            | 00  |   1 | SVM 1   | Pack/Unpack mode, SUBVL>1   |
             | 01  | inv | CR-bit  | Rc=1: ffirst CR sel              |
             | 01  | inv | VLi RC1 |  Rc=0: ffirst z/nonz |
-            | 10  |   N | dz   sz |  sat mode: N=0/1 u/s |
+            | 10  |   N | dz   sz |  sat mode: N=0/1 u/s, SUBVL=1 |
+            | 10  |   N | zz   0  |  sat mode: N=0/1 u/s, SUBVL>1 |
+            | 10  |   N | zz   1  |  Pack/Unpack sat mode: N=0/1 u/s, SUBVL>1 |
             | 11  | inv | CR-bit  |  Rc=1: pred-result CR sel |
-            | 11  | inv | dz  RC1 |  Rc=0: pred-result z/nonz |
+            | 11  | inv | zz  RC1 |  Rc=0: pred-result z/nonz |
         """
 
         # https://libre-soc.org/openpower/sv/ldst/
@@ -1096,9 +1221,6 @@ class SVP64Asm:
                 if is_ldst:
                     # TODO: for now, LD/ST-indexed is ignored.
                     mode |= ldst_elstride << SVP64MODE.ELS_NORMAL  # el-strided
-                    # shifted mode
-                    if ldst_shift:
-                        mode |= 1 << SVP64MODE.LDST_SHIFT
                 else:
                     # TODO, reduce and subvector mode
                     # 00  1   dz CRM  reduce mode (mapreduce), SUBVL=1
@@ -1170,29 +1292,29 @@ class SVP64Asm:
         # now put into svp64_rm
         mode |= sv_mode
         # mode: bits 19-23
-        svp64_rm.mode.eq(SelectableInt(mode, SVP64RM_MODE_SIZE))
+        svp64_rm.mode = mode
 
         # put in predicate masks into svp64_rm
         if ptype == '2P':
             # source pred: bits 16-18
-            svp64_rm.smask.eq(SelectableInt(smask, SVP64RM_SMASK_SIZE))
+            svp64_rm.smask = smask
         # mask mode: bit 0
-        svp64_rm.mmode.eq(SelectableInt(mmode, SVP64RM_MMODE_SIZE))
+        svp64_rm.mmode = mmode
         # 1-pred: bits 1-3
-        svp64_rm.mask.eq(SelectableInt(pmask, SVP64RM_MASK_SIZE))
+        svp64_rm.mask = pmask
 
         # and subvl: bits 8-9
-        svp64_rm.subvl.eq(SelectableInt(subvl, SVP64RM_SUBVL_SIZE))
+        svp64_rm.subvl = subvl
 
         # put in elwidths
         # srcwid: bits 6-7
-        svp64_rm.ewsrc.eq(SelectableInt(srcwid, SVP64RM_EWSRC_SIZE))
+        svp64_rm.ewsrc = srcwid
         # destwid: bits 4-5
-        svp64_rm.elwidth.eq(SelectableInt(destwid, SVP64RM_ELWIDTH_SIZE))
+        svp64_rm.elwidth = destwid
 
         # nice debug printout. (and now for something completely different)
         # https://youtu.be/u0WOIwlXE9g?t=146
-        svp64_rm_value = svp64_rm.spr.value
+        svp64_rm_value = int(svp64_rm)
         log("svp64_rm", hex(svp64_rm_value), bin(svp64_rm_value))
         log("    mmode  0    :", bin(mmode))
         log("    pmask  1-3  :", bin(pmask))
@@ -1215,18 +1337,34 @@ class SVP64Asm:
             log("    smask  16-17:", bin(smask))
         log()
 
-        # first, construct the prefix from its subfields
-        svp64_prefix = SVP64PrefixFields()
-        svp64_prefix.major.eq(SelectableInt(0x1, SV64P_MAJOR_SIZE))
-        svp64_prefix.pid.eq(SelectableInt(0b11, SV64P_PID_SIZE))
-        svp64_prefix.rm.eq(svp64_rm.spr)
+        # update prefix PO and ID (aka PID)
+        svp64_prefix.po = 0x1
+        svp64_prefix.id = 0b11
 
         # fiinally yield the svp64 prefix and the thingy.  v3.0b opcode
         rc = '.' if rc_mode else ''
-        yield ".long 0x%08x" % svp64_prefix.insn.value
-        log(v30b_newfields)
+        yield ".long 0x%08x" % int(svp64_prefix)
+        log(v30b_op, v30b_newfields)
+
+        v30b_op_rc = v30b_op
+        if not v30b_op.endswith('.'):
+            v30b_op_rc += rc
+
+        # svstep is weird
+        # FIXME(lkcl): should sv.svstep be like svstep?
+        if v30b_op_rc in ("svstep", "svstep."):
+            # compensate for `SVi -= 1` in svstep()
+            v30b_newfields[1] = str(int(v30b_newfields[1]) + 1)
+
+        custom_insn_hook = CUSTOM_INSNS.get(v30b_op_rc)
+        if custom_insn_hook is not None:
+            fields = tuple(map(to_number, v30b_newfields))
+            insn_num = custom_insn_hook(fields)
+            log(opcode, bin(insn_num))
+            yield ".long 0x%X # %s" % (insn_num, insn)
+            return
         # argh, sv.fmadds etc. need to be done manually
-        if v30b_op == 'ffmadds':
+        elif v30b_op == 'ffmadds':
             opcode = 59 << (32-6)    # bits 0..6 (MSB0)
             opcode |= int(v30b_newfields[0]) << (32-11)  # FRT
             opcode |= int(v30b_newfields[1]) << (32-16)  # FRA
@@ -1243,7 +1381,7 @@ class SVP64Asm:
             opcode |= int(v30b_newfields[1]) << (32-16)  # FRA
             opcode |= int(v30b_newfields[2]) << (32-21)  # FRB
             opcode |= int(v30b_newfields[3]) << (32-26)  # FRC
-            opcode |= 0b01111 << (32-31)   # bits 26-30
+            opcode |= 0b11011 << (32-31)   # bits 26-30
             if rc:
                 opcode |= 1  # Rc, bit 31.
             yield ".long 0x%x" % opcode
@@ -1253,34 +1391,14 @@ class SVP64Asm:
             opcode |= int(v30b_newfields[0]) << (32-11)  # FRT
             opcode |= int(v30b_newfields[1]) << (32-16)  # FRA
             opcode |= int(v30b_newfields[2]) << (32-21)  # FRB
-            opcode |= 0b01101 << (32-31)   # bits 26-30
+            opcode |= 0b1000001100 << (32-31)   # bits 21-30
             if rc:
                 opcode |= 1  # Rc, bit 31.
             yield ".long 0x%x" % opcode
-        # sigh have to do svstep here manually for now...
-        elif opcode in ["svstep", "svstep."]:
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            insn |= int(v30b_newfields[0]) << (31-10)  # RT       , bits 6-10
-            insn |= int(v30b_newfields[1]) << (31-22)  # SVi      , bits 16-22
-            insn |= int(v30b_newfields[2]) << (31-25)  # vf       , bit  25
-            insn |= 0b10011 << (31-30)  # XO       , bits 26..30
-            if opcode == 'svstep.':
-                insn |= 1 << (31-31)     # Rc=1     , bit 31
-            log("svstep", bin(insn))
-            yield ".long 0x%x" % insn
-        # argh, sv.fcoss etc. need to be done manually
-        elif v30b_op in ["fcoss", "fcoss."]:
-            insn = 59 << (31-5)  # opcode 59, bits 0-5
-            insn |= int(v30b_newfields[0]) << (31-10)  # RT       , bits 6-10
-            insn |= int(v30b_newfields[1]) << (31-20)  # RB       , bits 16-20
-            insn |= 0b1000101110 << (31-30)  # XO       , bits 21..30
-            if opcode == 'fcoss.':
-                insn |= 1 << (31-31)     # Rc=1     , bit 31
-            log("fcoss", bin(insn))
-            yield ".long 0x%x" % insn
-
         else:
-            yield "%s %s" % (v30b_op+rc, ", ".join(v30b_newfields))
+            if not v30b_op.endswith('.'):
+                v30b_op += rc
+            yield "%s %s" % (v30b_op, ", ".join(v30b_newfields))
         log("new v3.0B fields", v30b_op, v30b_newfields)
 
     def translate(self, lst):
@@ -1370,7 +1488,7 @@ def asm_process():
             macro = op[4:].split(",")
             (macro, value) = map(str.strip, macro)
             macros[macro] = value
-        if not op.startswith(tuple(CUSTOM_INSNS)):
+        if not op.startswith('sv.') and not op.startswith(tuple(CUSTOM_INSNS)):
             outfile.write(line)
             continue
 
@@ -1452,6 +1570,29 @@ if __name__ == '__main__':
         'cprop 3,12,5',
         'svindex 0,0,1,0,0,0,0',
     ]
+    lst = [
+        'sv.svstep./m=r3 2.v, 4, 0',
+        'ternlogi 0,0,0,0x5',
+        'fmvis 5,65535',
+        'fmvis 5,1',
+        'fmvis 5,2',
+        'fmvis 5,4',
+        'fmvis 5,8',
+        'fmvis 5,16',
+        'fmvis 5,32',
+        'fmvis 5,64',
+        'fmvis 5,32768',
+    ]
+    lst = [
+        'sv.andi. *80, *80, 1',
+        'sv.ffmadds. 6.v, 2.v, 4.v, 6.v',  # incorrectly inserted 32-bit op
+        'sv.ffmadds 6.v, 2.v, 4.v, 6.v',  # correctly converted to .long
+        'svshape2 8, 1, 31, 7, 1, 1',
+    ]
     isa = SVP64Asm(lst, macros=macros)
-    log("list", list(isa))
-    asm_process()
+    log("list:\n", "\n\t".join(list(isa)))
+    # running svp64.py is designed to test hard-coded lists
+    # (above) - which strictly speaking should all be unit tests.
+    # if you need to actually do assembler translation at the
+    # commandline use "pysvp64asm" - see setup.py
+    # XXX NO. asm_process()