fix fmvis decoder, it's now a 2-operand instruction
[openpower-isa.git] / src / openpower / sv / trans / svp64.py
index 620a9bdc5fc74440a15c2502f31de6b1cbb7549c..1a39f0914755322b498c398f9ce3bb30087b1fbb 100644 (file)
@@ -18,6 +18,7 @@ Encoding format of LDST: https://libre-soc.org/openpower/sv/ldst/
 Bugtracker: https://bugs.libre-soc.org/show_bug.cgi?id=578
 """
 
+import functools
 import os
 import sys
 from collections import OrderedDict
@@ -26,9 +27,12 @@ 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_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
@@ -39,6 +43,356 @@ from openpower.consts import SVP64MODE
 from openpower.util import log
 
 
+def instruction(*fields):
+    def instruction(insn, desc):
+        (value, start, end) = desc
+        bits = ((1,) * ((end + 1) - start))
+        mask = 0
+        for bit in bits:
+            mask = ((mask << 1) | bit)
+        return (insn | ((value & mask) << (31 - end)))
+
+    return functools.reduce(instruction, fields, 0)
+
+
+def setvl(fields, Rc):
+    """
+    setvl is a *32-bit-only* instruction. It controls SVSTATE.
+    It is *not* a 64-bit-prefixed Vector instruction (no sv.setvl, yet),
+    it is a Vector *control* instruction.
+
+    * setvl RT,RA,SVi,vf,vs,ms
+
+    1.6.28 SVL-FORM - from fields.txt
+    |0     |6    |11    |16   |23 |24 |25 |26    |31 |
+    | PO   |  RT |   RA | SVi |ms |vs |vf |   XO |Rc |
+    """
+    PO = 22
+    XO = 0b11011
+    # ARRRGH these are in a non-obvious order in openpower/isa/simplev.mdwn
+    # compared to the SVL-Form above. sigh
+    # setvl RT,RA,SVi,vf,vs,ms
+    (RT, RA, SVi, vf, vs, ms) = fields
+    SVi -= 1
+    return instruction(
+        (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),
+    )
+
+
+def svstep(fields, Rc):
+    """
+    svstep is a 32-bit instruction. It updates SVSTATE.
+    It *can* be SVP64-prefixed, to indicate that its registers
+    are Vectorised.
+
+    * svstep RT,SVi,vf
+
+    # 1.6.28 SVL-FORM - from fields.txt
+    # |0     |6    |11    |16   |23 |24 |25 |26    |31 |
+    # | PO   |  RT | /    | SVi |/  |/  |vf |   XO |Rc |
+
+    """
+    PO = 22
+    XO = 0b10011
+    (RT, SVi, vf) = fields
+    SVi -= 1
+    return instruction(
+        (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),
+    )
+
+
+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
+
+    # 1.6.33 SVM-FORM from fields.txt
+    # |0     |6        |11      |16    |21    |25 |26    |31  |
+    # | PO   |  SVxd   |   SVyd | SVzd | SVrm |vf |   XO      |
+
+    """
+    PO = 22
+    XO = 0b011001
+    (SVxd, SVyd, SVzd, SVrm, vf) = fields
+    SVxd -= 1
+    SVyd -= 1
+    SVzd -= 1
+    return instruction(
+        (PO  , 0 , 5 ),
+        (SVxd, 6 , 10),
+        (SVyd, 11, 15),
+        (SVzd, 16, 20),
+        (SVrm, 21, 24),
+        (vf  , 25, 25),
+        (XO  , 26, 31),
+    )
+
+
+def svindex(fields):
+    """
+    svindex is a *32-bit-only* instruction. It is a convenience
+    instruction that reduces instruction count for Indexed REMAP
+    Mode.
+    It is *not* a 64-bit-prefixed Vector instruction (no sv.svindex, yet),
+    it is a Vector *control* instruction.
+
+    1.6.28 SVI-FORM
+      |0     |6    |11    |16   |21 |23|24|25|26    31|
+      | PO   |  SVG|rmm   | SVd |ew |yx|mm|sk|   XO   |
+    """
+    # note that the dimension field one subtracted
+    PO = 22
+    XO = 0b101001
+    (SVG, rmm, SVd, ew, yx, mm, sk) = fields
+    SVd -= 1
+    return instruction(
+        (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),
+    )
+
+
+def svremap(fields):
+    """
+    this is a *32-bit-only* instruction. It updates the SVSHAPE SPR
+    it is *not* a 64-bit-prefixed Vector instruction (no sv.svremap),
+    it is a Vector *control* instruction.
+
+    * svremap SVme,mi0,mi1,mi2,mo0,mo1,pst
+
+    # 1.6.34 SVRM-FORM
+       |0     |6     |11  |13   |15   |17   |19   |21  |22   |26     |31 |
+       | PO   | SVme |mi0 | mi1 | mi2 | mo0 | mo1 |pst |///  | XO        |
+
+    """
+    PO = 22
+    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, 25),
+        (XO  , 26, 31),
+    )
+
+
+# ok from here-on down these are added as 32-bit instructions
+# and are here only because binutils (at present) doesn't have
+# them (that's being fixed!)
+# they can - if implementations then choose - be Vectorised
+# because they are general-purpose scalar instructions
+def bmask(fields):
+    """
+    1.6.2.2 BM2-FORM
+    |0     |6    |11    |16    |21   |26 |27    31|
+    | PO   |  RT |   RA |   RB |bm   |L  |   XO   |
+    """
+    PO = 22
+    XO = 0b010001
+    (RT, RA, RB, bm, L) = fields
+    return instruction(
+        (PO, 0 , 5 ),
+        (RT, 6 , 10),
+        (RA, 11, 15),
+        (RB, 16, 20),
+        (bm, 21, 25),
+        (L , 26, 26),
+        (XO, 27, 31),
+    )
+
+
+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         |     ///     |   FRB       |   XO |Rc  |
+    PO = 59
+    XO = 0b1000001110
+    (FRT, FRB) = fields
+    return instruction(
+        (PO , 0 , 5 ),
+        (FRT, 6 , 10),
+        (0  , 11, 15),
+        (FRB, 16, 20),
+        (XO , 21, 30),
+        (Rc , 31, 31),
+    )
+
+
+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         |     ///     |   FRB       |   XO |Rc  |
+    PO = 59
+    XO = 0b1000101110
+    (FRT, FRB) = fields
+    return instruction(
+        (PO , 0 , 5 ),
+        (FRT, 6 , 10),
+        (0  , 11, 15),
+        (FRB, 16, 20),
+        (XO , 21, 30),
+        (Rc , 31, 31),
+    )
+
+
+def ternlogi(fields, Rc):
+    # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
+    # however we are out of space with opcode 22
+    # 1.6.34 TLI-FORM
+    #   |0   |6   |11   |16   |21   |29  |31 |
+    #   | PO | RT |  RA |  RB | TLI | XO |Rc |
+    PO = 5
+    XO = 0
+    (RT, RA, RB, TLI) = fields
+    return instruction(
+        (PO , 0 , 5 ),
+        (RT , 6 , 10),
+        (RA , 11, 15),
+        (RB , 16, 20),
+        (TLI, 21, 28),
+        (XO , 29, 30),
+        (Rc , 31, 31),
+    )
+
+
+def grev(fields, Rc, imm, wide):
+    # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
+    # however we are out of space with opcode 22
+    insn = PO = 5
+    # _ matches fields in table at:
+    # https://libre-soc.org/openPOwer/sv/bitmanip/
+    XO = 0b1_0010_110
+    if wide:
+        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:
+        assert 0 <= XBI < 64
+        insn = (insn << 6) | XBI
+        insn = (insn << 9) | XO
+    else:
+        assert 0 <= XBI < 32
+        insn = (insn << 5) | XBI
+        insn = (insn << 10) | XO
+    insn = (insn << 1) | Rc
+    return insn
+
+
+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  |
+    #   | PO   |       RT      |    RA       |    RB       |   XO |Rc  |
+    PO = 22
+    (RT, RA, RB) = fields
+    return instruction(
+        (PO, 0 , 5 ),
+        (RT, 6 , 10),
+        (RA, 11, 15),
+        (RB, 16, 20),
+        (XO, 21, 30),
+        (Rc, 31, 31),
+    )
+
+
+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 = 0b000011
+    Rc = 0
+    (FRS, imm) = fields
+    return instruction(
+        (PO , 0 , 5),
+        (FRS, 6 , 10),
+        (imm, 11, 26),
+        (XO , 27, 30),
+        (Rc , 31, 31),
+    )
+
+
+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
+CUSTOM_INSNS["fmvis"] = fmvis
+
+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)
+
+
 # decode GPR into sv extra
 def get_extra_gpr(etype, regmode, field):
     if regmode == 'scalar':
@@ -131,8 +485,22 @@ def decode_ffirst(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:
@@ -152,6 +520,60 @@ def decode_imm(field):
         return None, field
 
 
+def crf_extra(etype, 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.
+    this function can be used for both BF/BFA and BA/BB/BT by first removing
+    the bottom 2 bits of BA/BB/BT then re-instating them after encoding.
+    see https://libre-soc.org/openpower/sv/svp64/appendix/#cr_extra
+    for specification
+    """
+    sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
+    # now sanity-check (and shrink afterwards)
+    if etype == 'EXTRA2':
+        # 3-bit CR Field (BF, BFA) EXTRA2 encoding
+        if regmode == 'scalar':
+            # range is CR0-CR15 in increments of 1
+            assert (sv_extra >> 1) == 0, \
+                "scalar CR %s cannot fit into EXTRA2 %s" % \
+                (rname, str(extras[extra_idx]))
+            # all good: encode as scalar
+            sv_extra = sv_extra & 0b01
+        else: # vector
+            # range is CR0-CR127 in increments of 16
+            assert sv_extra & 0b111 == 0, \
+                "vector CR %s cannot fit into EXTRA2 %s" % \
+                (rname, str(extras[extra_idx]))
+            # all good: encode as vector (bit 2 set)
+            sv_extra = 0b10 | (sv_extra >> 3)
+    else:
+        # 3-bit CR Field (BF, BFA) EXTRA3 encoding
+        if regmode == 'scalar':
+            # range is CR0-CR31 in increments of 1
+            assert (sv_extra >> 2) == 0, \
+                "scalar CR %s cannot fit into EXTRA3 %s" % \
+                (rname, str(extras[extra_idx]))
+            # all good: encode as scalar
+            sv_extra = sv_extra & 0b11
+        else: # vector
+            # range is CR0-CR127 in increments of 8
+            assert sv_extra & 0b11 == 0, \
+                "vector CR %s cannot fit into EXTRA3 %s" % \
+                (rname, str(extras[extra_idx]))
+            # all good: encode as vector (bit 3 set)
+            sv_extra = 0b100 | (sv_extra >> 2)
+    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)
+
+
 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
 class SVP64Asm:
     def __init__(self, lst, bigendian=False, macros=None):
@@ -186,180 +608,15 @@ class SVP64Asm:
             fields.append(macro_subst(macros, field))
         log("opcode, fields substed", ls, opcode, fields)
 
-        # this is a *32-bit-only* instruction. it controls SVSTATE.
-        # it is *not* a 64-bit-prefixed Vector instruction (no sv.setvl),
-        # it is a Vector *control* instruction.
-        # note: EXT022 is the "sandbox" major opcode so it's fine to add
-
-        # sigh have to do setvl here manually for now...
-        # note the subtract one from SVi.
-        if opcode in ["setvl", "setvl."]:
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            fields = list(map(int, fields))
-            insn |= fields[0] << (31-10)  # RT       , bits 6-10
-            insn |= fields[1] << (31-15)  # RA       , bits 11-15
-            insn |= (fields[2]-1) << (31-22)  # SVi      , bits 16-22
-            insn |= fields[3] << (31-25)  # ms       , bit  25
-            insn |= fields[4] << (31-24)  # vs       , bit  24
-            insn |= fields[5] << (31-23)  # vf       , bit  23
-            insn |= 0b00000 << (31-30)  # XO       , bits 26..30
-            if opcode == 'setvl.':
-                insn |= 1 << (31-31)     # Rc=1     , bit 31
-            log("setvl", bin(insn))
-            yield ".long 0x%x" % insn
-            return
-
-        # this is a *32-bit-only* instruction. it updates SVSTATE.
-        # it is *not* a 64-bit-prefixed Vector instruction (no sv.svstep),
-        # it is a Vector *control* instruction.
-        # note: EXT022 is the "sandbox" major opcode so it's fine to add
-
-        # sigh have to do setvl here manually for now...
-        # note the subtract one from SVi.
-        if opcode in ["svstep", "svstep."]:
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            fields = list(map(int, fields))
-            insn |= fields[0] << (31-10)  # RT       , bits 6-10
-            insn |= (fields[1]-1) << (31-22)  # SVi      , bits 16-22
-            insn |= fields[2] << (31-25)  # vf       , bit  25
-            insn |= 0b00011 << (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
-            return
-
-        # this is a *32-bit-only* instruction. it updates SVSHAPE and SVSTATE.
-        # it is *not* a 64-bit-prefixed Vector instruction (no sv.svshape),
-        # it is a Vector *control* instruction.
-        # note: EXT022 is the "sandbox" major opcode so it's fine to add
-
-        # and svshape.  note that the dimension fields one subtracted from each
-        if opcode == 'svshape':
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            fields = list(map(int, fields))
-            insn |= (fields[0]-1) << (31-10)  # SVxd       , bits 6-10
-            insn |= (fields[1]-1) << (31-15)  # SVyd       , bits 11-15
-            insn |= (fields[2]-1) << (31-20)  # SVzd       , bits 16-20
-            insn |= (fields[3]) << (31-24)  # SVRM       , bits 21-24
-            insn |= (fields[4]) << (31-25)  # vf         , bits 25
-            insn |= 0b00001 << (31-30)  # XO       , bits 26..30
-            #insn &= ((1<<32)-1)
-            log("svshape", bin(insn))
-            yield ".long 0x%x" % insn
-            return
-
-        # this is a *32-bit-only* instruction. it updates the SVSHAPE SPR
-        # it is *not* a 64-bit-prefixed Vector instruction (no sv.svremap),
-        # it is a Vector *control* instruction.
-        # note: EXT022 is the "sandbox" major opcode so it's fine to add
-
-        # and svremap
-        if opcode == 'svremap':
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            fields = list(map(int, fields))
-            insn |= fields[0] << (31-10)  # SVme       , bits 6-10
-            insn |= fields[1] << (31-12)  # mi0        , bits 11-12
-            insn |= fields[2] << (31-14)  # mi1        , bits 13-14
-            insn |= fields[3] << (31-16)  # mi2        , bits 15-16
-            insn |= fields[4] << (31-18)  # m00        , bits 17-18
-            insn |= fields[5] << (31-20)  # m01        , bits 19-20
-            insn |= fields[6] << (31-21)  # m01        , bit 21
-            insn |= 0b00010 << (31-30)  # XO       , bits 26..30
-            #insn &= ((1<<32)-1)
-            log("svremap", bin(insn))
-            yield ".long 0x%x" % insn
-            return
-
-        # ok from here-on down these are added as 32-bit instructions
-        # and are here only because binutils (at present) doesn't have
-        # them (that's being fixed!)
-        # they can - if implementations then choose - be Vectorised
-        # (sv.fsins) because they are general-purpose scalar instructions
-
-        # and fsins
-        # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
-        # however we are out of space with opcode 22
-        if opcode.startswith('fsins'):
-            fields = list(map(int, fields))
-            insn = 59 << (31-5)  # opcode 59, bits 0-5
-            insn |= fields[0] << (31-10)  # RT       , bits 6-10
-            insn |= fields[1] << (31-20)  # RB       , bits 16-20
-            insn |= 0b1000001110 << (31-30)  # XO       , bits 21..30
-            if opcode == 'fsins.':
-                insn |= 1 << (31-31)     # Rc=1     , bit 31
-            log("fsins", bin(insn))
-            yield ".long 0x%x" % insn
-            return
-
-        # and fcoss
-        # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
-        # however we are out of space with opcode 22
-        if opcode.startswith('fcoss'):
-            fields = list(map(int, fields))
-            insn = 59 << (31-5)  # opcode 59, bits 0-5
-            insn |= fields[0] << (31-10)  # RT       , bits 6-10
-            insn |= fields[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))
+        # identify if it is a special instruction
+        custom_insn_hook = CUSTOM_INSNS.get(opcode)
+        if custom_insn_hook is not None:
+            fields = tuple(map(to_number, fields))
+            insn = custom_insn_hook(fields)
+            log(opcode, bin(insn))
             yield ".long 0x%x" % insn
             return
 
-        # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
-        # however we are out of space with opcode 22
-        if opcode in ('ternlogi', 'ternlogi.'):
-            po = 5
-            xo = 0
-            rt = int(fields[0])
-            ra = int(fields[1])
-            rb = int(fields[2])
-            imm = int(fields[3])
-            rc = '.' in opcode
-            instr = po
-            instr = (instr << 5) | rt
-            instr = (instr << 5) | ra
-            instr = (instr << 5) | rb
-            instr = (instr << 8) | imm
-            instr = (instr << 2) | xo
-            instr = (instr << 1) | rc
-            asm = f"{opcode} {rt}, {ra}, {rb}, {imm}"
-            yield f".4byte {hex(instr)} # {asm}"
-            return
-
-        # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
-        # however we are out of space with opcode 22
-        if opcode in ('grev', 'grevi', 'grevw', 'grevwi',
-                      'grev.', 'grevi.', 'grevw.', 'grevwi.'):
-            po = 5
-            # _ matches fields in table at:
-            # https://libre-soc.org/openpower/sv/bitmanip/
-            xo = 0b1_0010_110
-            if 'w' in opcode:
-                xo |= 0b100_000
-            if 'i' in opcode:
-                xo |= 0b1000_000
-            Rc = 1 if '.' in opcode else 0
-            rt = int(fields[0])
-            ra = int(fields[1])
-            rb_imm = int(fields[2])
-            instr = po
-            instr = (instr << 5) | rt
-            instr = (instr << 5) | ra
-            if opcode == 'grevi' or opcode == 'grevi.':
-                assert 0 <= rb_imm < 64
-                instr = (instr << 6) | rb_imm
-                instr = (instr << 9) | xo
-            else:
-                assert 0 <= rb_imm < 32
-                instr = (instr << 5) | rb_imm
-                instr = (instr << 10) | xo
-            instr = (instr << 1) | Rc
-            asm = f"{opcode} {rt}, {ra}, {rb_imm}"
-            yield f".4byte {hex(instr)} # {asm}"
-            return
-
         # identify if is a svp64 mnemonic
         if not opcode.startswith('sv.'):
             yield insn  # unaltered
@@ -495,7 +752,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=" ")
 
@@ -505,6 +762,14 @@ class SVP64Asm:
             # XXX also TODO: the LD/ST modes which are different
             # https://libre-soc.org/openpower/sv/ldst/
 
+            # rright.  SVP64 register numbering is from 0 to 127
+            # for GPRs, FPRs *and* CR Fields, where for v3.0 the GPRs and RPFs
+            # are 0-31 and CR Fields are only 0-7.  the SVP64 RM "Extra"
+            # area is used to extend the numbering from the 32-bit
+            # instruction, and also to record whether the register
+            # is scalar or vector. on a per-operand basis.  this
+            # results in a slightly finnicky encoding: here we go...
+
             # encode SV-GPR and SV-FPR field into extra, v3.0field
             if rtype in ['GPR', 'FPR']:
                 sv_extra, field = get_extra_gpr(etype, regmode, field)
@@ -530,84 +795,26 @@ class SVP64Asm:
                     # EXTRA3 vector bit needs marking
                     sv_extra |= 0b100
 
-            # encode SV-CR 3-bit field into extra, v3.0field
+            # 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 = get_extra_cr_3bit(etype, regmode, field)
-                # now sanity-check (and shrink afterwards)
-                if etype == 'EXTRA2':
-                    if regmode == 'scalar':
-                        # range is CR0-CR15 in increments of 1
-                        assert (sv_extra >> 1) == 0, \
-                            "scalar CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as scalar
-                        sv_extra = sv_extra & 0b01
-                    else:
-                        # range is CR0-CR127 in increments of 16
-                        assert sv_extra & 0b111 == 0, \
-                            "vector CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as vector (bit 2 set)
-                        sv_extra = 0b10 | (sv_extra >> 3)
-                else:
-                    if regmode == 'scalar':
-                        # range is CR0-CR31 in increments of 1
-                        assert (sv_extra >> 2) == 0, \
-                            "scalar CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as scalar
-                        sv_extra = sv_extra & 0b11
-                    else:
-                        # range is CR0-CR127 in increments of 8
-                        assert sv_extra & 0b11 == 0, \
-                            "vector CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as vector (bit 3 set)
-                        sv_extra = 0b100 | (sv_extra >> 2)
+                sv_extra, field =  crf_extra(etype, regmode, field, extras)
 
             # encode SV-CR 5-bit field into extra, v3.0field
-            # *sigh* this is the same as 3-bit except the 2 LSBs are
-            # passed through
+            # 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
-                field = field >> 2  # strip bottom 2 bits
-                sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
-                # now sanity-check (and shrink afterwards)
-                if etype == 'EXTRA2':
-                    if regmode == 'scalar':
-                        # range is CR0-CR15 in increments of 1
-                        assert (sv_extra >> 1) == 0, \
-                            "scalar CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as scalar
-                        sv_extra = sv_extra & 0b01
-                    else:
-                        # range is CR0-CR127 in increments of 16
-                        assert sv_extra & 0b111 == 0, \
-                            "vector CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as vector (bit 2 set)
-                        sv_extra = 0b10 | (sv_extra >> 3)
-                else:
-                    if regmode == 'scalar':
-                        # range is CR0-CR31 in increments of 1
-                        assert (sv_extra >> 2) == 0, \
-                            "scalar CR %s cannot fit into EXTRA2 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as scalar
-                        sv_extra = sv_extra & 0b11
-                    else:
-                        # range is CR0-CR127 in increments of 8
-                        assert sv_extra & 0b11 == 0, \
-                            "vector CR %s cannot fit into EXTRA3 %s" % \
-                            (rname, str(extras[extra_idx]))
-                        # all good: encode as vector (bit 3 set)
-                        sv_extra = 0b100 | (sv_extra >> 2)
-                # reconstruct the actual 5-bit CR field
+                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)
+                # reconstruct the actual 5-bit CR field (preserving the
+                # bottom 2 bits, unaltered)
                 field = (field << 2) | cr_subfield
 
             else:
-                print("no type match", rtype)
+                raise Exception("no type match: %s" % rtype)
 
             # capture the extra field info
             log("=>", "%5s" % bin(sv_extra), field)
@@ -1060,8 +1267,8 @@ class SVP64Asm:
 
         # fiinally yield the svp64 prefix and the thingy.  v3.0b opcode
         rc = '.' if rc_mode else ''
-        yield ".long 0x%x" % svp64_prefix.insn.value
-        log(v30b_newfields)
+        yield ".long 0x%08x" % svp64_prefix.insn.value
+        log(v30b_op, v30b_newfields)
         # argh, sv.fmadds etc. need to be done manually
         if v30b_op == 'ffmadds':
             opcode = 59 << (32-6)    # bits 0..6 (MSB0)
@@ -1095,32 +1302,17 @@ class SVP64Asm:
                 opcode |= 1  # Rc, bit 31.
             yield ".long 0x%x" % opcode
         # sigh have to do svstep here manually for now...
-        elif opcode in ["svstep", "svstep."]:
+        elif v30b_op 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 |= 0b00011 << (31-30)  # XO       , bits 26..30
+            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
-
-        elif v30b_op in ["setvl", "setvl."]:
-            insn = 22 << (31-5)          # opcode 22, bits 0-5
-            fields = list(map(int, fields))
-            insn |= fields[0] << (31-10)  # RT       , bits 6-10
-            insn |= fields[1] << (31-15)  # RA       , bits 11-15
-            insn |= (fields[2]-1) << (31-22)  # SVi      , bits 16-22
-            insn |= fields[3] << (31-25)  # ms       , bit  25
-            insn |= fields[4] << (31-24)  # vs       , bit  24
-            insn |= fields[5] << (31-23)  # vf       , bit  23
-            insn |= 0b00000 << (31-30)  # XO       , bits 26..30
-            if opcode == 'setvl.':
-                insn |= 1 << (31-31)     # Rc=1     , bit 31
-            log("setvl", 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
@@ -1130,7 +1322,6 @@ class SVP64Asm:
                 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))
         log("new v3.0B fields", v30b_op, v30b_newfields)
@@ -1142,38 +1333,38 @@ class SVP64Asm:
 
 def macro_subst(macros, txt):
     again = True
-    print("subst", txt, macros)
+    log("subst", txt, macros)
     while again:
         again = False
         for macro, value in macros.items():
             if macro == txt:
                 again = True
                 replaced = txt.replace(macro, value)
-                print("macro", txt, "replaced", replaced, macro, value)
+                log("macro", txt, "replaced", replaced, macro, value)
                 txt = replaced
                 continue
             toreplace = '%s.s' % macro
             if toreplace == txt:
                 again = True
                 replaced = txt.replace(toreplace, "%s.s" % value)
-                print("macro", txt, "replaced", replaced, toreplace, value)
+                log("macro", txt, "replaced", replaced, toreplace, value)
                 txt = replaced
                 continue
             toreplace = '%s.v' % macro
             if toreplace == txt:
                 again = True
                 replaced = txt.replace(toreplace, "%s.v" % value)
-                print("macro", txt, "replaced", replaced, toreplace, value)
+                log("macro", txt, "replaced", replaced, toreplace, value)
                 txt = replaced
                 continue
             toreplace = '(%s)' % macro
             if toreplace in txt:
                 again = True
                 replaced = txt.replace(toreplace, '(%s)' % value)
-                print("macro", txt, "replaced", replaced, toreplace, value)
+                log("macro", txt, "replaced", replaced, toreplace, value)
                 txt = replaced
                 continue
-    print("    processed", txt)
+    log("    processed", txt)
     return txt
 
 
@@ -1189,7 +1380,6 @@ def get_ws(line):
 
 
 def asm_process():
-
     # get an input file and an output file
     args = sys.argv[1:]
     if len(args) == 0:
@@ -1198,7 +1388,7 @@ def asm_process():
         # read the whole lot in advance in case of in-place
         lines = list(infile.readlines())
     elif len(args) != 2:
-        print("pysvp64asm [infile | -] [outfile | -]")
+        print("pysvp64asm [infile | -] [outfile | -]", file=sys.stderr)
         exit(0)
     else:
         if args[0] == '--':
@@ -1213,36 +1403,24 @@ def asm_process():
         else:
             outfile = open(args[1], "w")
 
-    # read the line, look for "sv", process it
+    # read the line, look for custom insn, process it
     macros = {}  # macros which start ".set"
     isa = SVP64Asm([])
     for line in lines:
-        ls = line.split("#")
+        op = line.split("#")[0].strip()
         # identify macros
-        op = ls[0].strip()
-        if op.startswith("setvl") or op.startswith("svshape"):
-            ws, line = get_ws(ls[0])
-            lst = list(isa.translate_one(ls[0].strip(), macros))
-            lst = '; '.join(lst)
-            outfile.write("%s%s # %s\n" % (ws, lst, ls[0]))
-            continue
-        if ls[0].startswith(".set"):
-            macro = ls[0][4:].split(",")
-            macro, value = list(map(str.strip, macro))
+        if op.startswith(".set"):
+            macro = op[4:].split(",")
+            (macro, value) = map(str.strip, macro)
             macros[macro] = value
-        if len(ls) != 2:
-            outfile.write(line)
-            continue
-        potential = ls[1].strip()
-        if not potential.startswith("sv."):
+        if not op.startswith('sv.') and not op.startswith(tuple(CUSTOM_INSNS)):
             outfile.write(line)
             continue
 
-        ws, line = get_ws(line)
-        # SV line indentified
-        lst = list(isa.translate_one(potential, macros))
+        (ws, line) = get_ws(line)
+        lst = isa.translate_one(op, macros)
         lst = '; '.join(lst)
-        outfile.write("%s%s # %s\n" % (ws, lst, potential))
+        outfile.write("%s%s # %s\n" % (ws, lst, op))
 
 
 if __name__ == '__main__':
@@ -1304,8 +1482,23 @@ if __name__ == '__main__':
         'sv.bc/all 3,12,192',
         'sv.bclr/vsbi 3,81.v,192',
         'sv.ld 5.v, 4(1.v)',
+        'sv.svstep. 2.v, 4, 0',
+    ]
+    lst = [
+        'maxs 3,12,5',
+        'maxs. 3,12,5',
+        'avgadd 3,12,5',
+        'absdu 3,12,5',
+        'absds 3,12,5',
+        'absdacu 3,12,5',
+        'absdacs 3,12,5',
+        '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'
     ]
     isa = SVP64Asm(lst, macros=macros)
-    print("list", list(isa))
-    csvs = SVP64RM()
-    # asm_process()
+    log("list", list(isa))
+    asm_process()