#!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/ -I. -Ilib/ -I/home/phil/perl/cpan/AsmC/lib/
#-------------------------------------------------------------------------------
# Generate X86 assembler code using Perl as a macro pre-processor.
# Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021-2022
#-------------------------------------------------------------------------------
# podDocumentation (\A.{80})\s+(#.*\Z) \1\2        (^sub.*#.*[^.]$) \1.
# 0x401000 from sde-mix-out addresses to get offsets in z.txt
# tree::print - speed up decision as to whether we are on a tree or not
# Make hash accept parameters at: #THash
# Document that V > 0 is required to create a boolean test
# Make sure that we are using bts and bzhi as much as possible in mask situations
# Replace calls to Tree::position with Tree::down
# Do not use r11 over extended ranges because Linux sets it to the flags register on syscalls. Free: rsi rdi, r11, rbx, rcx, rdx, likewise the mmx registers mm0-7, zmm 0..15 and k0..3.
# Make a tree read only - collapse all nodes possible, remove all leaf node arrays
# Jump forwarding
# All options checking immediately after parameters
# Which subs do we actually need SaveFirst four on?
# Binary search tighten up register saves
# github.com/<username>/<repo_name>/compare/<commit1>...<commit2>
# https://github.com/philiprbrenan/NasmX86/compare/9bb6e05..09d1ec9
# Variable::at to replace addrExpr
package Nasm::X86;
our $VERSION = "20220712";
use warnings FATAL => qw(all);
use strict;
use Carp qw(confess cluck);
use Data::Dump qw(dump);
use Data::Table::Text qw(:all);
use Time::HiRes qw(time);
use feature qw(say current_sub);
use utf8;

makeDieConfess unless &onGitHub;

my %rodata;                                                                     # Read only data already written
my %rodatas;                                                                    # Read only string already written
my %subroutines;                                                                # Subroutines generated
my @rodata;                                                                     # Read only data
my @data;                                                                       # Data
my @bss;                                                                        # Block started by symbol
my @text;                                                                       # Code
my @extern;                                                                     # External symbols imports for linking with C libraries
my @link;                                                                       # Specify libraries which to link against in the final assembly stage
my $interpreter  = q(-I /usr/lib64/ld-linux-x86-64.so.2);                       # The ld command needs an interpreter if we are linking with C.
our $sdeMixOut   = q(sde-mix-out.txt);                                          # Emulator hot spot output file
our $sdeTraceOut = q(sde-debugtrace-out.txt);                                   # Emulator trace output file
our $sdePtrCheck = q(sde-ptr-check.out.txt);                                    # Emulator pointer check file
our $traceBack   = q(zzzTraceBack.txt);                                         # Trace back of last error observed in emulator trace file if tracing is on
our $programErr  = q(zzzErr.txt);                                               # Program error  file
our $programOut  = q(zzzOut.txt);                                               # Program output file
our $sourceFile  = q(z.asm);                                                    # Source file

our $stdin       = 0;                                                           # File descriptor for standard input
our $stdout      = 1;                                                           # File descriptor for standard output
our $stderr      = 2;                                                           # File descriptor for standard error

our $TraceMode   = 0;                                                           # 1: writes trace data into rax after every instruction to show the call stack by line number in this file for the instruction being executed.  This information is then visible in the sde trace from whence it is easily extracted to give a trace back for instructions executed in this mode.  This mode assumes that you will not be using the mm0 register (most people are not)and that you have any IDE like Geany that can interpret a Perl error line number and position on that line in this file.
our $DebugMode   = 0;                                                           # 1: enables checks that take time and sometimes catch programming errors.
our $LibraryMode = 0;                                                           # 1: we are building a library so constants must appear in line in the text section rather than in a block in the data section

my %Registers;                                                                  # The names of all the registers
my %RegisterContaining;                                                         # The largest register containing a register
my @GeneralPurposeRegisters = qw(rax rbx rcx rdx rsi rdi), map {"r$_"} 8..15;   # General purpose registers
my $bitsInByte;                                                                 # The number of bits in a byte
my @declarations;                                                               # Register and instruction declarations
my %loadAreaIntoAssembly;                                                       # Areas already loaded by file name

BEGIN{
  $bitsInByte  = 8;                                                             # The number of bits in a byte
  my %r = (    map {$_=>[ 8,  '8'  ]}  qw(al bl cl dl r8b r9b r10b r11b r12b r13b r14b r15b r8l r9l r10l r11l r12l r13l r14l r15l sil dil spl bpl ah bh ch dh));
     %r = (%r, map {$_=>[16,  's'  ]}  qw(cs ds es fs gs ss));
     %r = (%r, map {$_=>[16,  '16' ]}  qw(ax bx cx dx r8w r9w r10w r11w r12w r13w r14w r15w si di sp bp));
     %r = (%r, map {$_=>[32,  '32a']}  qw(eax  ebx ecx edx esi edi esp ebp));
     %r = (%r, map {$_=>[32,  '32b']}  qw(r8d r9d r10d r11d r12d r13d r14d r15d));
     %r = (%r, map {$_=>[80,  'f'  ]}  qw(st0 st1 st2 st3 st4 st5 st6 st7));
     %r = (%r, map {$_=>[64,  '64' ]}  qw(rax rbx rcx rdx r8 r9 r10 r11 r12 r13 r14 r15 rsi rdi rsp rbp rip rflags));
     %r = (%r, map {$_=>[64,  '64m']}  qw(mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7));
     %r = (%r, map {$_=>[128, '128']}  qw(xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31));
     %r = (%r, map {$_=>[256, '256']}  qw(ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31));
     %r = (%r, map {$_=>[512, '512']}  qw(zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31));
     %r = (%r, map {$_=>[64,  'm'  ]}  qw(k0 k1 k2 k3 k4 k5 k6 k7));

  %Registers = %r;                                                              # Register names

  my sub registerContaining($@)
   {my ($r, @r) = @_;                                                           # Register, contents
    $RegisterContaining{$r} = $r;                                               # A register contains itself
    $RegisterContaining{$_} = $r for @r;                                        # Registers contained by a register
   }

  registerContaining("k$_")                                            for 0..7;
  registerContaining("zmm$_",   "ymm$_", "xmm$_")                      for 0..31;
  registerContaining("r${_}x", "e${_}x", "${_}x",  "${_}l",  "${_}h")  for qw(a b c d);
  registerContaining("r${_}",  "r${_}l", "r${_}w", "r${_}b", "r${_}d") for 8..15;
  registerContaining("r${_}p", "e${_}p", "${_}p",  "${_}pl")           for qw(s b);
  registerContaining("r${_}i", "e${_}i", "${_}i", "${_}il")            for qw(s d);
  my @i0 = qw(cpuid lahf leave popfq pushfq rdtsc ret syscall);                 # Zero operand instructions

  my @i1 = split /\s+/, <<END;                                                  # Single operand instructions
align bswap call dec div idiv  inc jmp ja jae jb jbe jc jcxz je jecxz jg jge jl jle
jna jnae jnb jnbe jnc jne jng jnge jnl jnle jno jnp jns jnz jo jp jpe jpo jrcxz
js jz loop neg not seta setae setb setbe setc sete setg setge setl setle setna setnae
setnb setnbe setnc setne setng setnge setnl setno setnp setns setnz seto setp
setpe setpo sets setz pop push
include
incbin
END

  my @i2 =  split /\s+/, <<END;                                                 # Double operand instructions
add and bsf bsr bt btc btr bts
cmova cmovae cmovb cmovbe cmovc cmove cmovg cmovge cmovl cmovle
cmovna cmovnae cmovnb cmovne cmp
enter
imul
kmov knot kortest ktest lea lzcnt mov movd movq movw  movdqa
or popcnt sal sar shl shr sub test tzcnt
vcvtudq2pd vcvtuqq2pd vcvtudq2ps vmovdqu vmovdqu32 vmovdqu64 vmovdqu8
vpcompressd vpcompressq vpexpandd vpexpandq xchg xor
vmovd vmovq
mulpd
pslldq psrldq
vpgatherqd vpgatherqq
vpmovb2m vpmovw2m Vpmovd2m vpmovq2m

vsqrtpd
vmovdqa32 vmovdqa64
END
# print STDERR join ' ', sort @i2; exit;

  my @i2qdwb =  split /\s+/, <<END;                                             # Double operand instructions which have qdwb versions
vpmovm2
vpbroadcast
END

  my @i3 =  split /\s+/, <<END;                                                 # Triple operand instructions
andn
bzhi
imul3
kadd kand kandn kor kshiftl kshiftr kunpck kxnor kxor
pdep pext
vdpps
vprolq
vgetmantps
vaddd
vmulpd vaddpd
END

  my @i3qdwb =  split /\s+/, <<END;                                             # Triple operand instructions which have qdwb versions
pinsr pextr valign vpand vpandn vpcmpeq vpor vpxor vptest vporvpcmpeq vpinsr vpextr vpadd vpsub vpmull
END

  my @i4 =  split /\s+/, <<END;                                                 # Quadruple operand instructions
END

  my @i4qdwb =  split /\s+/, <<END;                                             # Quadruple operand instructions which have qdwb versions
vpcmpu
END

  if (1)                                                                        # Add variants to mask instructions
   {my @k2  = grep {m/\Ak/} @i2; @i2  = grep {!m/\Ak/} @i2;
    my @k3  = grep {m/\Ak/} @i3; @i3  = grep {!m/\Ak/} @i3;
    for my $s(qw(b w d q))
     {push @i2, $_.$s for grep {m/\Ak/} @k2;
      push @i3, $_.$s for grep {m/\Ak/} @k3;
     }
   }

  if (1)                                                                        # Add qdwb versions of instructions
   {for my $o(@i2qdwb)
     {push @i2, $o.$_ for qw(b w d q);
     }
    for my $o(@i3qdwb)
     {push @i3, $o.$_ for qw(b w d q);
     }
    for my $o(@i4qdwb)
     {push @i4, $o.$_ for qw(b w d q);
     }
   }

  for my $r(sort keys %r)                                                       # Create register definitions
   {if (1)
     {my $s = "sub $r\{q($r)\}";
      push @declarations, $r;
      eval $s;
      confess "$s$@ "if $@;
     }
    if (1)
     {my $b = $r{$r}[0] / $bitsInByte;
      my $s = "sub ${r}Size\{$b}";
      eval $s;
      confess "$s$@ "if $@;
     }
   }

  my %v = map {$$_[1]=>1} values %r;
  for my $v(sort keys %v)                                                       # Types of register
   {my @r = grep {$r{$_}[1] eq $v} sort keys %r;
    my $s = "sub registers_$v\{".dump(\@r)."}";
    eval $s;
    confess "$s$@" if $@;
   }

  if (1)                                                                        # Instructions that take zero operands
   {my $s = '';
    for my $i(@i0)
      {my $I = ucfirst $i;
       push @declarations, $I;
       $s .= <<END;
       sub $I()
        {\@_ == 0 or confess "No arguments allowed";
         push \@text, qq($i\\n);
        }
END
     }
    eval $s;
    confess "$s$@" if $@;
   }

  if (1)                                                                        # Instructions that take one operand
   {my $s = '';
    for my $i(@i1)
      {my $I = ucfirst $i;
       push @declarations, $I;
       $s .= <<END;
       sub $I
        {my (\$target) = \@_;
         \@_ == 1 or confess "One argument required, not ".scalar(\@_);
         push \@text, qq($i \$target\\n);
        }
END
     }
    eval $s;
    confess "$s$@" if $@;
   }

  if (1)                                                                        # Instructions that take two operands
   {my $s = '';
    for my $i(@i2)
      {my $I = ucfirst $i;
       push @declarations, $I;
       $s .= <<END;
       sub $I(\@)
        {my (\$target, \$source) = \@_;
         \@_ == 2 or confess "Two arguments required, not ".scalar(\@_);
         &traceInstruction(q($i), \$source, \$target);
         push \@text, qq($i \$target, \$source\\n);
        }
END
     }
    eval $s;
    confess "$s$@" if $@;
   }

  if (1)                                                                        # Instructions that take three operands
   {my $s = '';
    for my $i(@i3)
      {my $I = ucfirst $i;
       push @declarations, $I;
       my $j = $i =~ s(\d\Z) ()r;                                               # Remove number of parameters designated
       $s .= <<END;
       sub $I(\@)
        {my (\$target, \$source, \$bits) = \@_;
         \@_ == 3 or confess "Three arguments required, not ".scalar(\@_);
         push \@text, qq($j \$target, \$source, \$bits\\n);
        }
END
     }
    eval "$s$@";
    confess $@ if $@;
   }

  if (1)                                                                        # Instructions that take four operands
   {my $s = '';
    for my $i(@i4)
      {my $I = ucfirst $i;
       push @declarations, $I;
       $s .= <<END;
       sub $I(\@)
        {my (\$target, \$source, \$bits, \$zero) = \@_;
         \@_ == 4 or confess "Four arguments required, not ".scalar(\@_);
         push \@text, qq($i \$target, \$source, \$bits, \$zero\\n);
        }
END
     }
    eval "$s$@";
    confess $@ if $@;
   }

  sub traceInstruction($$$)                                                     # Trace the location of this instruction in  the source code
   {my ($i, $source, $target) = @_;                                             # Instruction
    return unless $TraceMode and $i =~ m(\Amov\Z);                              # Trace just these instructions and only when tracing is enabled
    my @c;
    confess "zmm register in mov" if $source =~ m(\Azmm);                       # A particularly difficult error to track down

    push @text, <<END;                                                          # Tracing destroys mm0 so that we can use r11
  movq mm0, r11;
END

    for(1..100)                                                                 # Write line numbers of traceback
     {my @c = caller $_;
      last unless @c;
      $c[3] =~ s(Nasm::X86::) ();
      my (undef, undef, $line, $file) = @c;
      push @text, <<END;
  mov r11, $line;
END
     }

    push @text, <<END;                                                          # Restore r11 from destroyed mm0
  movq r11, mm0;
END
   }
 }

sub byteRegister($)                                                             # The byte register corresponding to a full size register.
 {my ($r) = @_;                                                                 # Full size register
  if ($r =~ m(\Ar([abcd])x\Z)) {return $1."l"};
  return dil if $r eq rdi;
  return sil if $r eq rsi;
  $r."b"
 }

sub wordRegister($)                                                             # The word register corresponding to a full size register.
 {my ($r) = @_;                                                                 # Full size register
  if ($r =~ m(\Ar([abcd])x\Z)) {return $1."x"};
  return di if $r eq rdi;
  return si if $r eq rsi;
  $r."w"
 }

sub dWordRegister($)                                                            # The double word register corresponding to a full size register.
 {my ($r) = @_;                                                                 # Full size register
  if ($r =~ m(\Ar([abcd])x\Z)) {return "e".$1."x"};
  return edi if $r eq rdi;
  return esi if $r eq rsi;
  $r."d"
 }

#D1 Labels                                                                      # Create and set labels.

my $Labels = 0;

sub Label()                                                                     # Create a unique label. Useful for constructing for and if statements.
 {"l".++$Labels unless @_;                                                      # Generate a label
 }

sub SetLabel(;$)                                                                # Create (if necessary) and set a label in the code section returning the label so set.
 {my ($l) = @_;                                                                 # Label
  $l //= Label;
  push @text, <<END;                                                            # Define bytes
  $l:
END
  $l                                                                            # Return label name
 }

#D1 Data                                                                        # Layout data


#D2 Global variables                                                            # Create variables in the data segment if you are willing to make your program non reentrant.

sub Ds(@)                                                                       # Layout bytes in memory and return their label.
 {my (@d) = @_;                                                                 # Data to be laid out
  my $d = join '', @_;
     $d =~ s(') (\')gs;
  my $l = Label;
  push @data, <<END;                                                            # Define bytes
  $l: db  '$d';
END
  $l                                                                            # Return label
 }

sub Dbwdq($@)                                                                   #P Layout data.
 {my ($s, @d) = @_;                                                             # Element size, data to be laid out
  my $d = join ', ', @d;
confess "AAAA" if $s eq "q" and $d =~ m(zmm31);
  my $l = Label;
  push @data, <<END;
  $l: d$s $d
END
  $l                                                                            # Return label
 }

sub Db(@)                                                                       # Layout bytes in the data segment and return their label.
 {my (@bytes) = @_;                                                             # Bytes to layout
  Dbwdq 'b', @_;
 }
sub Dw(@)                                                                       # Layout words in the data segment and return their label.
 {my (@words) = @_;                                                             # Words to layout
  Dbwdq 'w', @_;
 }
sub Dd(@)                                                                       # Layout double words in the data segment and return their label.
 {my (@dwords) = @_;                                                            # Double words to layout
  Dbwdq 'd', @_;
 }
sub Dq(@)                                                                       # Layout quad words in the data segment and return their label.
 {my (@qwords) = @_;                                                            # Quad words to layout
  Dbwdq 'q', @_;
 }

#D2 Global constants                                                            # Create constants in read only memory,

sub Rbwdq($@)                                                                   #P Layout data.
 {my ($s, @d) = @_;                                                             # Element size, data to be laid out
  my $d = join ', ', map {$_ =~ m(\A\d+\Z) ? sprintf "0x%x", $_ : $_} @d;       # Data to be laid out
  if (my $c = $rodata{$s}{$d})                                                  # Data already exists so return it
   {return $c
   }
  if ($LibraryMode)                                                             # Create data in a library - we put it inline so that is copied with the position independent subroutine after optimizing the jumps just before assembly.
   {my $l = Label; my $x = Label;                                               # New data - create a label for the data  and then jump over it as it is in the code section -- we will have to optimize jumps later
    push @text, <<END;                                                          # Save in read only data
    Jmp $x;
    $l: d$s $d
    $x:
END
    $rodata{$s}{$d} = $l;                                                       # Record label
    return $l;
   }
  else
   {my $l = Label;                                                              # New data - create a label
    push @rodata, <<END;                                                        # Save in read only data
    $l: d$s $d
END
    $rodata{$s}{$d} = $l;                                                       # Record label
    return $l;
   }
 }

sub Rb(@)                                                                       # Layout bytes in the data segment and return their label.
 {my (@bytes) = @_;                                                             # Bytes to layout
  Rbwdq 'b', @_;
 }
sub Rw(@)                                                                       # Layout words in the data segment and return their label.
 {my (@words) = @_;                                                             # Words to layout
  Rbwdq 'w', @_;
 }
sub Rd(@)                                                                       # Layout double words in the data segment and return their label.
 {my (@dwords) = @_;                                                            # Double words to layout
  Rbwdq 'd', @_;
 }
sub Rq(@)                                                                       # Layout quad words in the data segment and return their label.
 {my (@qwords) = @_;                                                            # Quad words to layout
  Rbwdq 'q', @_;
 }

sub Rs(@)                                                                       # Layout bytes in read only memory and return their label.
 {my (@d) = @_;                                                                 # Data to be laid out
  my $d = join '', @_;
  my @e;
  for my $e(split //, $d)
   {if ($e !~ m([A-Z0-9])i) {push @e, sprintf("0x%x", ord($e))} else {push @e, qq('$e')}
   }
  my $e = join ', ', @e;
  my $L = $rodatas{$e};
  return $L if defined $L;                                                      # Data already exists so return it

  if ($LibraryMode)                                                             # Create data in a library - we put it inline so that is copied with the position independent subroutine after optimizing the jumps just before assembly.
   {my $l = Label; my $x = Label;                                               # New label for new data
    $rodatas{$e} = $l;                                                          # Record label
    push @text, <<END;                                                          # Define bytes
  Jmp $x;
  $l: db  $e, 0;
  $x:
END
    return $l;                                                                  # Return label
   }
  else
   {my $l = Label;                                                              # New label for new data
    $rodatas{$e} = $l;                                                          # Record label
    push @rodata, <<END;                                                        # Define bytes
  $l: db  $e, 0;
END
    return $l                                                                   # Return label
   }
 }

sub Rutf8(@)                                                                    # Layout a utf8 encoded string as bytes in read only memory and return their label.
 {my (@d) = @_;                                                                 # Data to be laid out
  confess unless @_;
  my $d = join '', @_;
  my @e;
  for my $e(split //, $d)
   {my $o  = ord $e;                                                            # Effectively the utf32 encoding of each character
    my $u  = convertUtf32ToUtf8($o);
    my $x  = sprintf("%08x", $u);
    my $o1 = substr($x, 0, 2);
    my $o2 = substr($x, 2, 2);
    my $o3 = substr($x, 4, 2);
    my $o4 = substr($x, 6, 2);

    if    ($o <= (1 << 7))  {push @e,                $o4}
    elsif ($o <= (1 << 11)) {push @e,           $o3, $o4}
    elsif ($o <= (1 << 16)) {push @e,      $o2, $o3, $o4}
    else                    {push @e, $o1, $o2, $o3, $o4}
   }

  my $e = join ', ',map {"0x$_"}  @e;
  my $L = $rodatas{$e};
  return $L if defined $L;                                                      # Data already exists so return it
  my $l = Label;                                                                # New label for new data
  $rodatas{$e} = $l;                                                            # Record label
  push @rodata, <<END;                                                          # Define bytes
  $l: db  $e, 0;
END
  $l                                                                            # Return label
 }

my $Pi = "3.141592653589793238462";

sub Pi32 {Rd("__float32__($Pi)")}                                               #P Pi as a 32 bit float.
sub Pi64 {Rq("__float32__($Pi)")}                                               #P Pi as a 64 bit float.

#D1 Registers                                                                   # Operations on registers

#D2 Size                                                                        # Sizes of each register

sub RegisterSize($)                                                             # Return the size of a register.
 {my ($R) = @_;                                                                 # Register
  my $r = &registerNameFromNumber($R);
  defined($r) or confess;
  defined($Registers{$r}) or confess "No such registers as: $r";
  eval "${r}Size()";
 }

sub qSize {8}                                                                   # Size of a quad word in bytes
sub dSize {4}                                                                   # Size of a double word in bytes
sub wSize {2}                                                                   # Size of a word in bytes
sub bSize {1}                                                                   # Size of a byte in bytes

#D2 Push and Pop                                                                # Generic versions of push and pop with pop popping the last push.

sub PushRR(@)                                                                   #P Push registers onto the stack without tracking.
 {my (@r) = @_;                                                                 # Register
  my $w = RegisterSize rax;
  my @p;                                                                        # Non zmm registers
  my @z;                                                                        # Zmm registers
  for my $r(map {&registerNameFromNumber($_)} @r)
   {if ($r =~ m(\Azmm))                                                         # Do zmm's last as they can be optimized
     {unshift @z, $r;
     }
    else                                                                        # Non zmm registers
     {push @p, $r;
      my $size = RegisterSize $r;
      $size or confess "No such register: $r";
      if    ($size > $w)                                                        # Wide registers
       {Sub rsp, $size;
        Vmovdqu64 "[rsp]", $r;
       }
      elsif ($r =~ m(\Ak))                                                      # Mask as they do not respond to push
       {Sub rsp, $size;
        Kmovq "[rsp]", $r;
       }
      else                                                                      # Normal register
       {Push $r;
       }
     }
   }
  if (@z)                                                                       # Zmm registers
   {my $w = RegisterSize(zmm0);                                                 # Register width
    Sub rsp, $w * @z;                                                           # Reduce stack to make room for zmm registers being pushed
    for my $i(keys @z)                                                          # Copy each zmm register being pushed to the stack
     {Vmovdqu64 "[rsp+$i*$w]", $z[$i];
     }
   }
  my @R = (@p, reverse @z);                                                     # Actual push sequence - for some strange reason we have to put it in a variable first
 }

my @PushR;                                                                      # Track pushes

sub PushR(@)                                                                    # Push registers onto the stack.
 {my (@r) = @_;                                                                 # Registers
  my @p = PushRR @r;                                                            # Push
  push @PushR, [@p];                                                            # Registers pushed we can pop them in order
  scalar(@PushR)                                                                # Stack depth
 }

sub PopRR(@)                                                                    #P Pop registers from the stack without tracking.
 {my (@r) = @_;                                                                 # Register
  my $w = RegisterSize rax;
  my $W = RegisterSize zmm0;
  my $z = 0;                                                                    # Offset in stack of zmm register
  @r = reverse map{&registerNameFromNumber($_)} @r;                             # Pop registers in reverse order- any zmm registers will be first

  for my $r(@r)                                                                 # Pop registers in reverse order- any zmm registers will be first
   {if ($r =~ m(\Azmm))                                                         # The zmm registers come first and are popped by offset
     {Vmovdqu64 $r, "[rsp+$z]";
      $z += $W;
     }
   }
  Add rsp, $z if $z;                                                            # Move up over any zmm registers

  for my $r(@r)                                                                 # Pop non zmm registers in reverse order
   {my $size = RegisterSize $r;
    if    ($size == $W) {}                                                      # The zmm registers have already been popped
    elsif    ($size > $w)                                                       # Xmm, ymm
     {Vmovdqu64 $r, "[rsp]";
      Add rsp, $size;
     }
    elsif ($r =~ m(\Ak))                                                        # Mask registers
     {Kmovq $r, "[rsp]";
      Add rsp, $size;
     }
    else                                                                        # General purpose registers
     {Pop $r;
     }
   }
 }

sub PopR(@)                                                                     # Pop registers from the stack. Use the last stored set if none explicitly supplied.  Pops are done in reverse order to match the original pushing order.
 {my (@r) = @_;                                                                 # Register
  @PushR or confess "No stacked registers";
  my $r = pop @PushR;
  dump(\@r) eq dump($r) or confess "Mismatched registers:\n".dump($r, \@r) if @r;
  PopRR @$r;                                                                    # Pop registers from the stack without tracking
 }

sub registerNameFromNumber($)                                                   #P Register name from number where possible.
 {my ($r) = @_;                                                                 # Register number
  return "zmm$r" if $r =~ m(\A(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\Z);
  return   "r$r" if $r =~ m(\A(8|9|10|11|12|13|14|15)\Z);
  return   "k$r" if $r =~ m(\A(0|1|2|3|4|5|6|7)\Z);
  $r
 }

sub ChooseRegisters($@)                                                         #P Choose the specified numbers of registers excluding those on the specified list.
 {my ($number, @registers) = @_;                                                # Number of registers needed, Registers not to choose
  my %r = (map {$_=>1} map {"r$_"} 8..15);
  delete $r{$_} for @registers;
  $number <= keys %r or confess "Not enough registers available";
  sort keys %r
 }

#D2 Save and Restore                                                            # Saving and restoring registers via the stack

my @syscallSequence = qw(rax rdi rsi rdx r10 r8 r9);                            # The parameter list sequence for system calls

sub SaveFirstFour(@)                                                            # Save the first 4 parameter registers making any parameter registers read only.
 {my (@keep) = @_;                                                              # Registers to mark as read only
  my $N = 4;
  PushRR $_ for @syscallSequence[0..$N-1];
  $N * &RegisterSize(rax);                                                      # Space occupied by push
 }

sub RestoreFirstFour()                                                          # Restore the first 4 parameter registers.
 {my $N = 4;
  PopRR $_ for reverse @syscallSequence[0..$N-1];
 }

sub RestoreFirstFourExceptRax()                                                 # Restore the first 4 parameter registers except rax so it can return its value.
 {my $N = 4;
  PopRR $_ for reverse @syscallSequence[1..$N-1];
  Add rsp, 1*RegisterSize(rax);
 }

sub SaveFirstSeven()                                                            # Save the first 7 parameter registers.
 {my $N = 7;
  PushRR $_ for @syscallSequence[0..$N-1];
  $N * 1*RegisterSize(rax);                                                     # Space occupied by push
 }

sub RestoreFirstSeven()                                                         # Restore the first 7 parameter registers.
 {my $N = 7;
  PopRR $_ for reverse @syscallSequence[0..$N-1];
 }

sub RestoreFirstSevenExceptRax()                                                # Restore the first 7 parameter registers except rax which is being used to return the result.
 {my $N = 7;
  PopRR $_ for reverse @syscallSequence[1..$N-1];
  Add rsp, 1*RegisterSize(rax);
 }

sub ClearRegisters(@)                                                           # Clear registers by setting them to zero.
 {my (@registers) = @_;                                                         # Registers
  my $w = RegisterSize rax;
  for my $r(map{registerNameFromNumber $_} @registers)                          # Each register
   {my $size = RegisterSize $r;
    Xor    $r, $r     if $size == $w and $r !~ m(\Ak);
    Kxorq  $r, $r, $r if $size == $w and $r =~ m(\Ak);
    Vpxorq $r, $r, $r if $size  > $w;
   }
 }

#D2 Zero flag                                                                   # Actions on the Zero Flag.

sub SetZF()                                                                     # Set the zero flag.
 {Cmp rax, rax;
 }

sub ClearZF()                                                                   # Clear the zero flag.
 {PushR rax;
  Mov rax, 1;
  Cmp rax, 0;
  PopR rax;
 }

#D2 x, y, zmm                                                                   # Actions specific to mm registers

sub xmm(@)                                                                      # Add xmm to the front of a list of register expressions.
 {my (@r) = @_;                                                                 # Register numbers
  map {"xmm$_"} @_;
 }

sub ymm(@)                                                                      # Add ymm to the front of a list of register expressions.
 {my (@r) = @_;                                                                 # Register numbers
  map {"ymm$_"} @_;
 }

sub zmm(@)                                                                      # Add zmm to the front of a list of register expressions.
 {my (@r) = @_;                                                                 # Register numbers
  map {m/\Azmm/ ? $_ : "zmm$_"} @_;
 }

sub zmmM($$)                                                                    # Add zmm to the front of a register number and a mask after it.
 {my ($z, $m) = @_;                                                             # Zmm number, mask register
  return "zmm$z\{k$m}" if $z =~ m(\A\d+\Z)    && $m =~ m(\A\d\Z);
  return "zmm$z\{$m}"  if $z =~ m(\A\d+\Z)    && $m =~ m(\Ak\d\Z);
  return "$z\{k$m}"    if $z =~ m(\Azmm\d+\Z) && $m =~ m(\A\d\Z);
  return "$z\{$m}"     if $z =~ m(\Azmm\d+\Z) && $m =~ m(\Ak\d\Z);
  confess "Bad zmm and mask specification: $z $m";
 }

sub zmmMZ($$)                                                                   # Add zmm to the front of a register number and mask and zero after it.
 {my ($z, $m) = @_;                                                             # Zmm number, mask register number
  return "zmm$z\{k$m}\{z}" if $z =~ m(\A\d+\Z)    && $m =~ m(\A\d\Z);
  return "zmm$z\{$m}\{z}"  if $z =~ m(\A\d+\Z)    && $m =~ m(\Ak\d\Z);
  return "$z\{k$m}\{z}"    if $z =~ m(\Azmm\d+\Z) && $m =~ m(\A\d\Z);
  return "$z\{$m}\{z}"     if $z =~ m(\Azmm\d+\Z) && $m =~ m(\Ak\d\Z);
  confess "Bad zmm and mask with zero specification: $z $m";
 }

#D3 Via general purpose registers                                               # Load zmm registers from data held in the general purpose registers.

sub LoadZmm($@)                                                                 # Load a numbered zmm with the specified bytes.
 {my ($zmm, @bytes) = @_;                                                       # Numbered zmm, bytes
  my $b = Rb(@bytes);
  Vmovdqu8 zmm($zmm), "[$b]";
 }

sub checkZmmRegister($)                                                         #P Check that a register is a zmm register.
 {my ($z) = @_;                                                                 # Parameters
  $z =~ m(\A(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\Z) or confess "$z is not the number of a zmm register";
 }

sub bRegFromZmm($$$)                                                            # Load the specified register from the byte at the specified offset located in the numbered zmm.
 {my ($register, $zmm, $offset) = @_;                                           # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or
    confess "Offset $offset Out of range";

  my $b = byteRegister $register;                                               # Corresponding byte register
  my $W = RegisterSize zmm0;                                                    # Register size
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov $b, "[rsp-$W+$offset]";                                                   # Load byte register from offset
 }

sub bRegIntoZmm($$$)                                                            # Put the byte content of the specified register into the byte in the numbered zmm at the specified offset in the zmm.
 {my ($register,  $zmm, $offset) = @_;                                          # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or confess "Out of range";

  my $b = byteRegister $register;                                               # Corresponding byte register
  my $W = RegisterSize zmm0;                                                    # Register size
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov "[rsp-$W+$offset]", $b;                                                   # Save byte at specified offset
  Vmovdqu32  zmm($zmm), "[rsp-$W]";
 }

sub wRegFromZmm($$$)                                                            # Load the specified register from the word at the specified offset located in the numbered zmm.
 {my ($register, $zmm, $offset) = @_;                                           # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or confess "Out of range";

  my $W = RegisterSize zmm0;                                                    # Register size
  my $w = wordRegister $register;                                               # Corresponding word register
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov $w, "[rsp-$W+$offset]";                                                   # Load word register from offset
 }

sub wRegIntoZmm($$$)                                                            # Put the specified register into the word in the numbered zmm at the specified offset in the zmm.
 {my ($register,  $zmm, $offset) = @_;                                          # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or confess "Out of range";

  my $w = wordRegister $register;                                               # Corresponding word register
  my $W = RegisterSize zmm0;                                                    # Register size
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov "[rsp-$W+$offset]", $w;                                                   # Save word at specified offset
  Vmovdqu32  zmm($zmm), "[rsp-$W]";
 }

sub dRegFromZmm($$$)                                                            # Load the specified register from the double word at the specified offset located in the numbered zmm.
 {my ($register, $zmm, $offset) = @_;                                           # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or confess "Out of range";

  my $W = RegisterSize zmm0;                                                    # Register size
  my $w = dWordRegister $register;                                              # Corresponding double word register
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov $w, "[rsp-$W+$offset]";                                                   # Load double word register from offset
 }

sub dRegIntoZmm($$$)                                                            # Put the specified register into the double word in the numbered zmm at the specified offset in the zmm.
 {my ($register,  $zmm, $offset) = @_;                                          # Register to load, numbered zmm register to load from, constant offset in bytes

  $offset >= 0 && $offset <= RegisterSize zmm0 or confess "Out of range";

  my $w = dWordRegister $register;                                              # Corresponding word register
  my $W = RegisterSize zmm0;                                                    # Register size
  Vmovdqu32 "[rsp-$W]", zmm $zmm;
  Mov "[rsp-$W+$offset]", $w;                                                   # Save double word at specified offset
  Vmovdqu32  zmm($zmm), "[rsp-$W]";
 }

sub LoadRegFromMm($$$)                                                          #P Load the specified register from the numbered zmm at the quad offset specified as a constant number.
 {my ($mm, $offset, $reg) = @_;                                                 # Mm register, offset in quads, general purpose register to load

  my $w = RegisterSize rax;                                                     # Size of rax
  my $W = RegisterSize $mm;                                                     # Size of mm register
  Vmovdqu64 "[rsp-$W]", $mm;                                                    # Write below the stack
  Mov $reg, "[rsp+$w*$offset-$W]";                                              # Load register from offset
 }

sub SaveRegIntoMm($$$)                                                          # Save the specified register into the numbered zmm at the quad offset specified as a constant number.
 {my ($mm, $offset, $reg) = @_;                                                 # Mm register, offset in quads, general purpose register to load

  my $w = RegisterSize rax;                                                     # Size of rax
  my $W = RegisterSize $mm;                                                     # Size of mm register
  Vmovdqu64 "[rsp-$W]", $mm;                                                    # Write below the stack
  Mov "[rsp+$w*$offset-$W]", $reg;                                              # Save register into offset
  Vmovdqu64 $mm, "[rsp-$W]";                                                    # Reload from the stack
 }

#D3 Via variables                                                               # Load zmm registers from data held in variables

sub extractRegisterNumberFromMM($)                                              #P Extract the register number from an *mm register.
 {my ($mm) = @_;                                                                # Mmm register
      $mm =~ m(\A([zyx]mm)?(\d{1,2})\Z) ? $2 : confess "Not an mm register";
 }

sub getBwdqFromMm($$$$%)                                                        #P Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable.
 {my ($xyz, $size, $mm, $offset, %options) = @_;                                # Size of mm, size of get, mm register, offset in bytes either as a constant or as a variable, options
  my $set = $options{set};                                                      # Optionally set this register or variable rather than returning a new variable
  my $setVar = $set && ref($set) =~ m(Variable);                                # Set a this variable to the result

  my $n = extractRegisterNumberFromMM $mm;                                      # Register number or fail if not an mm register
  my $m = $xyz.'mm'.$n;                                                         # Full name of register
  if (!ref($offset) and $offset == 0 and $set and !$setVar)                     # Use Pextr in this special circumstance - need to include other such
   {my $d = dWordRegister $set;                                                 # Target a dword register as set is not a variable
    push @text, <<END;
vpextr$size $d, xmm$n, 0
END
    return;
   }

  my $r = $setVar ? rdi : ($set // rdi);                                        # Choose a work register

  my $o;                                                                        # The offset into the mm register
  if (ref($offset))                                                             # The offset is being passed in a variable
   {$offset->setReg($o = rsi);
    confess "Cannot use rsi"  if $r eq rsi;                                     # Rsi is the offset to apply if a variable offset is supplied so we cannot use rsi in these circumstances as the target register
   }
  else                                                                          # The offset is being passed as a register expression
   {$o = $offset;
   }

  my $w = RegisterSize $m;                                                      # Size of mm register
  Vmovdqu32 "[rsp-$w]", $m;                                                     # Write below the stack

  ClearRegisters $r if $size !~ m(q|d);                                         # Clear the register if necessary
  Mov  byteRegister($r), "[rsp+$o-$w]" if $size =~ m(b);                        # Load byte register from offset
  Mov  wordRegister($r), "[rsp+$o-$w]" if $size =~ m(w);                        # Load word register from offset
  Mov dWordRegister($r), "[rsp+$o-$w]" if $size =~ m(d);                        # Load double word register from offset
  Mov $r,                "[rsp+$o-$w]" if $size =~ m(q);                        # Load register from offset

  if ($setVar)                                                                  # Set the supplied variable
   {$set->copy($r);
    return;
   }

  V("$size at offset $offset in $m", $r) unless $set;                           # Create variable unless a target register has been supplied
 }

sub bFromX($$)                                                                  # Get the byte from the numbered xmm register and return it in a variable.
 {my ($xmm, $offset) = @_;                                                      # Numbered xmm, offset in bytes
  getBwdqFromMm('x', 'b', "xmm$xmm", $offset)                                   # Get the numbered byte|word|double word|quad word from the numbered xmm register and return it in a variable
 }

sub wFromX($$)                                                                  # Get the word from the numbered xmm register and return it in a variable.
 {my ($xmm, $offset) = @_;                                                      # Numbered xmm, offset in bytes
  getBwdqFromMm('x', 'w', "xmm$xmm", $offset)                                   # Get the numbered byte|word|double word|quad word from the numbered xmm register and return it in a variable
 }

sub dFromX($$)                                                                  # Get the double word from the numbered xmm register and return it in a variable.
 {my ($xmm, $offset) = @_;                                                      # Numbered xmm, offset in bytes
  getBwdqFromMm('x', 'd', "xmm$xmm", $offset)                                   # Get the numbered byte|word|double word|quad word from the numbered xmm register and return it in a variable
 }

sub qFromX($$)                                                                  # Get the quad word from the numbered xmm register and return it in a variable.
 {my ($xmm, $offset) = @_;                                                      # Numbered xmm, offset in bytes
  getBwdqFromMm('x', 'q', "xmm$xmm", $offset)                                   # Get the numbered byte|word|double word|quad word from the numbered xmm register and return it in a variable
 }

sub bFromZ($$%)                                                                 # Get the byte from the numbered zmm register and return it in a variable.
 {my ($zmm, $offset, %options) = @_;                                            # Numbered zmm, offset in bytes, options
#  my $z = registerNameFromNumber $zmm;
  getBwdqFromMm('z', 'b', $zmm, $offset, %options)                              # Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable
 }

sub wFromZ($$%)                                                                 # Get the word from the numbered zmm register and return it in a variable.
 {my ($zmm, $offset, %options) = @_;                                            # Numbered zmm, offset in bytes,options
#  my $z = registerNameFromNumber $zmm;
  getBwdqFromMm('z', 'w', $zmm, $offset, %options)                              # Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable
 }

sub dFromZ($$%)                                                                 # Get the double word from the numbered zmm register and return it in a variable.
 {my ($zmm, $offset, %options) = @_;                                            # Numbered zmm, offset in bytes, options
#  my $z = extractRegisterNumberFromMM $zmm;
  getBwdqFromMm('z', 'd', $zmm, $offset, %options)                              # Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable
 }

sub qFromZ($$%)                                                                 # Get the quad word from the numbered zmm register and return it in a variable.
 {my ($zmm, $offset, %options) = @_;                                            # Numbered zmm, offset in bytes, options
#  my $z = registerNameFromNumber $zmm;
  getBwdqFromMm('z', 'q', $zmm, $offset, %options)                              # Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable
 }

#D2 Mask                                                                        # Operations on mask registers

sub SetMaskRegister($$$)                                                        # Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.
 {my ($mask, $start, $length) = @_;                                             # Number of mask register to set, register containing start position or 0 for position 0, register containing end position
  @_ == 3 or confess "Three parameters";
  $mask =~ m(\Ak?[0-7]\Z) or confess "Not a mask register: $mask";

  PushR 15, 14;
  Mov r15, -1;
  if ($start)                                                                   # Non zero start
   {Mov  r14, $start;
    Bzhi r15, r15, r14;
    Not  r15;
    Add  r14, $length;
   }
  else                                                                          # Starting at zero
   {Mov r14, $length;
   }
  Bzhi r15, r15, r14;
  Kmovq "k$mask", r15;
  PopR;
 }

sub LoadConstantIntoMaskRegister($$)                                            #P Set a mask register equal to a constant.
 {my ($mask, $value) = @_;                                                      # Number of mask register to load, constant to load
  @_ == 2 or confess "Two parameters";
  $mask =~ m(\Ak?[0-7]\Z) or confess "Not the number of a mask register: $mask";
  my $m = registerNameFromNumber $mask;
  Mov rdi, $value;                                                              # Load mask into a general purpose register
  Kmovq $m, rdi;                                                                # Load mask register from general purpose register
 }

sub createBitNumberFromAlternatingPattern($@)                                   #P Create a number from a bit pattern.
 {my ($prefix, @values) = @_;                                                   # Prefix bits, +n 1 bits -n 0 bits
  @_ > 1 or confess "Four or more parameters required";                         # Must have some values

  $prefix =~ m(\A[01]*\Z) or confess "Prefix must be binary";                   # Prefix must be binary
  @values = grep {$_ != 0} @values;                                             # Remove zeroes as they would produce no string

  for my $i(0..$#values-1)                                                      # Check each value alternates with the following values
   {($values[$i] > 0 && $values[$i+1] > 0  or
     $values[$i] < 0 && $values[$i+1] < 0) and confess "Signs must alternate";
   }

  my $b = "0b$prefix";
  for my $v(@values)                                                            # String representation of bit string
   {$b .= '1' x +$v if $v > 0;
    $b .= '0' x -$v if $v < 0;
   }

  my $n = eval $b;
  confess $@ if $@;
  $n
 }

sub LoadBitsIntoMaskRegister($$@)                                               # Load a bit string specification into a mask register in two clocks.
 {my ($mask, $prefix, @values) = @_;                                            # Number of mask register to load, prefix bits, +n 1 bits -n 0 bits
  @_ > 2 or confess "Three or more parameters required";                        # Must have some values

  LoadConstantIntoMaskRegister                                                  # Load the specified binary constant into a mask register
    ($mask, createBitNumberFromAlternatingPattern $prefix, @values)
 }

#D3 At a point                                                                  # Load data into a zmm register at the indoicated point and retrieve data fromn a zmm regisiter at the indicated ppint.

sub InsertZeroIntoRegisterAtPoint($$)                                           # Insert a zero into the specified register at the point indicated by another general purpose or mask register moving the higher bits one position to the left.
 {my ($point, $in) = @_;                                                        # Register with a single 1 at the insertion point, register to be inserted into.

  ref($point) and confess "Point must be a register";

  my $mask = rdi, my $low = rsi, my $high = rbx;                                # Choose three work registers and push them
  if ($point =~ m(\Ak?[0-7]\Z))                                                 # Mask register showing point
   {Kmovq $mask, $point;
   }
  else                                                                          # General purpose register showing point
   {Mov  $mask, $point;
   }

  Dec  $mask;                                                                   # Fill mask to the right of point with ones
  Andn $high, $mask, $in;                                                       # Part of in be shifted
  Shl  $high, 1;                                                                # Shift high part
  And  $in,  $mask;                                                             # Clear high part of target
  Or   $in,  $high;                                                             # Or in new shifted high part
 }

sub InsertOneIntoRegisterAtPoint($$)                                            # Insert a one into the specified register at the point indicated by another register.
 {my ($point, $in) = @_;                                                        # Register with a single 1 at the insertion point, register to be inserted into.
  InsertZeroIntoRegisterAtPoint($point, $in);                                   # Insert a zero
  if ($point =~ m(\Ak?[0-7]\Z))                                                 # Mask register showing point
   {my ($r) = ChooseRegisters(1, $in);                                          # Choose a general purpose register to place the mask in
    Kmovq rsi, $point;
    Or   $in, rsi;                                                              # Make the zero a one
   }
  else                                                                          # General purpose register showing point
   {Or $in, $point;                                                             # Make the zero a one
   }
 }

#D1 Comparison codes                                                            # The codes used to specify what sort of comparison to perform

my $Vpcmp = genHash("Nasm::X86::CompareCodes",                                  # Compare codes for "Vpcmp"
  eq=>0,                                                                        # Equal
  lt=>1,                                                                        # Less than
  le=>2,                                                                        # Less than or equals
  ne=>4,                                                                        # Not equals
  ge=>5,                                                                        # Greater than or equal
  gt=>6,                                                                        # Greater than
 );

# Forward declarations

sub BinarySearchD(%);
sub Comment(@);
sub CreateArea(%);
sub K($$);
sub PrintErrStringNL(@);
sub Subroutine(&%);
sub V($;$);

#D1 Structured Programming                                                      # Structured programming constructs

#D2 If                                                                          # If statements

sub If($$;$)                                                                    # If statement.
 {my ($jump, $then, $else) = @_;                                                # Jump op code of variable, then - required , else - optional
  @_ >= 2 && @_ <= 3 or confess;

  ref($jump) or $jump =~ m(\AJ(c|e|g|ge|h|l|le|nc|ne|ns|nz|s|z)\Z)
             or confess "Invalid jump: $jump";

  if (ref($jump))                                                               # Variable expression,  if it is non zero perform the then block else the else block
   {if (ref($jump) =~ m(scalar)i)                                               # Type of jump opposes the boolean operator
     { __SUB__->($$jump, $then, $else);
     }
    else                                                                        # Anything other than a scalar reference indicates that the 'If' statement was handed something other than a Boolean expression
     {confess "Not a boolean expression";
     }
   }
  elsif (!$else)                                                                # No else
   {my $end = Label;
    push @text, <<END;
    $jump $end;
END
    &$then;
    SetLabel $end;
   }
  else                                                                          # With else
   {my $endIf     = Label;
    my $startElse = Label;
    push @text, <<END;
    $jump $startElse
END
    &$then;
    Jmp $endIf;
    SetLabel $startElse;
    &$else;
    SetLabel  $endIf;
   }
 }

sub Then(&)                                                                     # Then block for an If statement.
 {my ($block) = @_;                                                             # Then block
  $block;
 }

sub Else(&)                                                                     # Else block for an If statement.
 {my ($block) = @_;                                                             # Else block
  $block;
 }

sub opposingJump($)                                                             #P Return the opposite of a jump.
 {my ($j) = @_;                                                                 # Jump
  my %j = qw(Je Jne  Jl Jge  Jle Jg  Jne Je  Jge Jl  Jg Jle);                   # Branch possibilities
  my $o = $j{$j};
  confess "Invalid jump $j" unless $o;
  $o
 }

sub ifOr($$;$)                                                                  # Execute then or else block based on a multiplicity of OR conditions executed until one succeeds.
 {my ($conditions, $Then, $Else) = @_;                                          # Array of conditions, then sub, else sub

  my $test = Label;                                                             # Start of test block
  my $then = Label;                                                             # Start of then block
  my $else = Label;                                                             # Start of else block
  my $end  = Label;                                                             # End of block

  Jmp $test;                                                                    # Jump over then and else
  SetLabel $then;                                                               # Then block
  &$Then;
  Jmp $end;
  SetLabel $else;
  &$Else if $Else;
  Jmp $end;

  SetLabel $test;                                                               # Start of tests

  for my $c(@$conditions)
   {my $j = opposingJump ${&$c};
    push @text, qq($j $then\n);
   }
  Jmp $else if $Else;
  SetLabel $end;
 }

sub ifAnd($$;$)                                                                 # Execute then or else block based on a multiplicity of AND conditions executed until one fails.
 {my ($conditions, $Then, $Else) = @_;                                          # Array of conditions, then sub, else sub

  my $test = Label;                                                             # Start of test block
  my $then = Label;                                                             # Start of then block
  my $else = Label;                                                             # Start of else block
  my $end  = Label;                                                             # End of block

  Jmp $test;                                                                    # Jump over then and else
  SetLabel $then;                                                               # Then block
  &$Then;
  Jmp $end;
  SetLabel $else;
  &$Else if $Else;
  Jmp $end;

  SetLabel $test;                                                               # Start of tests

  for my $c(@$conditions)
   {my $j = ${&$c};
    push @text, qq($j $else\n) if     $Else;
    push @text, qq($j $end\n)  unless $Else;
   }
  Jmp $then;
  SetLabel $end;
 }

sub Ef(&$;$)                                                                    # Else if block for an If statement.
 {my ($condition, $then, $else) = @_;                                           # Condition, then block, else block
  sub
  {If (&$condition, $then, $else);
  }
 }

#D3 Via flags                                                                   # If depending on the flags register.

sub IfEq($;$)                                                                   # If equal execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jne), $then, $else);                                                     # Opposite code
 }

sub IfNe($;$)                                                                   # If not equal execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Je), $then, $else);                                                      # Opposite code
 }

sub IfNz($;$)                                                                   # If the zero flag is not set then execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jz), $then, $else);                                                      # Opposite code
 }

sub IfZ($;$)                                                                    # If the zero flag is set then execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jnz), $then, $else);                                                     # Opposite code
 }

sub IfC($;$)                                                                    # If the carry flag is set then execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jnc), $then, $else);                                                     # Opposite code
 }

sub IfNc($;$)                                                                   # If the carry flag is not set then execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jc), $then, $else);                                                      # Opposite code
 }

sub IfLt($;$)                                                                   # If less than execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jge), $then, $else);                                                     # Opposite code
 }

sub IfLe($;$)                                                                   # If less than or equal execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jg), $then, $else);                                                      # Opposite code
 }

sub IfGt($;$)                                                                   # If greater than execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jle), $then, $else);                                                     # Opposite code
 }

sub IfGe($;$)                                                                   # If greater than or equal execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jl), $then, $else);                                                      # Opposite code
 }

sub IfS($;$)                                                                    #P If signed greater than or equal execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Jns), $then, $else);                                                     # Opposite code
 }

sub IfNs($;$)                                                                   #P If signed less than execute the then block else the else block.
 {my ($then, $else) = @_;                                                       # Then - required , else - optional
  If(q(Js), $then, $else);                                                      # Opposite code
 }

#D2 Boolean Blocks                                                              # Perform blocks depending on boolean conditions

sub Pass(&)                                                                     # Pass block for an L<OrBlock>.
 {my ($block) = @_;                                                             # Block
  $block;
 }

sub Fail(&)                                                                     # Fail block for an L<AndBlock>.
 {my ($block) = @_;                                                             # Block
  $block;
 }

sub Block(&)                                                                    # Execute a block of code with labels supplied for the start and end of this code.
 {my ($code) = @_;                                                              # Block of code
  @_ == 1 or confess "One parameter";
  SetLabel(my $start = Label);                                                  # Start of block
  my $end  = Label;                                                             # End of block
  &$code($end, $start);                                                         # Code with labels supplied
  SetLabel $end;                                                                # End of block
 }

sub AndBlock(&;$)                                                               # Short circuit B<and>: execute a block of code to test conditions which, if all of them pass, allows the first block to continue successfully else if one of the conditions fails we execute the optional fail block.
 {my ($test, $fail) = @_;                                                       # Block, optional failure block
  @_ == 1 or @_ == 2 or confess "One or two parameters";
  SetLabel(my $start = Label);                                                  # Start of test block
  my $Fail = @_ == 2 ? Label : undef;                                           # Start of fail block
  my $end  = Label;                                                             # End of both blocks
  &$test(($Fail // $end), $end, $start);                                        # Test code plus success code
  if ($fail)
   {Jmp $end;                                                                   # Skip the fail block if we succeed in reaching the end of the test block which is the expected behavior for short circuited B<and>.
    SetLabel $Fail;
    &$fail($end, $Fail, $start);                                                # Execute when true
   }
  SetLabel $end;                                                                # Exit block
 }

sub OrBlock(&;$)                                                                # Short circuit B<or>: execute a block of code to test conditions which, if one of them is met, leads on to the execution of the pass block, if all of the tests fail we continue withe the test block.
 {my ($test, $pass) = @_;                                                       # Tests, optional block to execute on success
  @_ == 1 or @_ == 2 or confess "One or two parameters";
  SetLabel(my $start = Label);                                                  # Start of test block
  my $Pass = @_ == 2 ? Label : undef;                                           # Start of pass block
  my $end  = Label;                                                             # End of both blocks
  &$test(($Pass // $end), $end, $start);                                        # Test code plus fail code
  if ($pass)
   {Jmp $end;                                                                   # Skip the pass block if we succeed in reaching the end of the test block which is the expected behavior for short circuited B<or>.
    SetLabel $Pass;
    &$pass($end, $Pass, $start);                                                # Execute when true
   }
  SetLabel $end;                                                                # Exit block
 }

#D2 Iteration                                                                   # Iterate with for loops

sub For(&$$;$)                                                                  # For - iterate the block as long as register is less than limit incrementing by increment each time. Nota Bene: The register is not explicitly set to zero as you might want to start at some other number.
 {my ($block, $register, $limit, $increment) = @_;                              # Block, register, limit on loop, increment on each iteration
  @_ == 3 or @_ == 4 or confess;
  $increment //= 1;                                                             # Default increment
  my $next = Label;                                                             # Next iteration
  Comment "For $register $limit";
  my $start = Label;
  my $end   = Label;
  SetLabel $start;
  Cmp $register, $limit;
  Jge $end;

  &$block($start, $end, $next);                                                 # Start, end and next labels

  SetLabel $next;                                                               # Next iteration starting with after incrementing
  if ($increment == 1)
   {Inc $register;
   }
  else
   {Add $register, $increment;
   }
  Jmp $start;                                                                   # Restart loop
  SetLabel $end;                                                                # Exit loop
 }

sub ToZero(&$)                                                                  # Iterate a block the number of times specified in the register which is decremented to zero.
 {my ($block, $register) = @_;                                                  # Block, limit register
  @_ == 2 or confess "Two parameters";
  my $end   = Label;                                                            # Next iteration
  my $start = SetLabel;
  Cmp $register, 0;
  Je $end;
  &$block($end, $start);                                                        # End and start labels
  Dec $register;
  Jne $start;
  SetLabel $end;
 }

sub ForIn(&$$$$)                                                                # For - iterate the full block as long as register plus increment is less than than limit incrementing by increment each time then perform the last block with the remainder which might be of length zero.
 {my ($full, $last, $register, $limitRegister, $increment) = @_;                # Block for full block, block for last block , register, register containing upper limit of loop, increment on each iteration

  my $start = Label;
  my $end   = Label;

  SetLabel $start;                                                              # Start of loop
  PushR $register;                                                              # Save the register so we can test that there is still room
  Add   $register, $increment;                                                  # Add increment
  Cmp   $register, $limitRegister;                                              # Test that we have room for increment
  PopR  $register;                                                              # Remove increment
  Jg   $end;

  &$full;

  Add $register, $increment;                                                    # Increment for real
  Jmp $start;
  SetLabel $end;

  Sub $limitRegister, $register;                                                # Size of remainder
#  IfNz
#  Then                                                                         # Execute remainder
   {&$last;                                                                     # Register shows position of remainder while $limit register shows amount left to process
   }
 }

sub uptoNTimes(&$$)                                                             # Execute a block of code up to a constant number of times controlled by the named register.
 {my ($code, $register, $limit) = @_;                                           # Block of code, register controlling loop, constant limit
  confess "Limit must be a positive integer constant"                           # Check for a specific limit
    unless $limit =~ m(\A\d+\Z) and $limit > 0;

  Mov $register, $limit;                                                        # Set the limit
  SetLabel(my $start = Label);                                                  # Start of block
  my $end  = Label;                                                             # End of block
  &$code($end, $start);                                                         # Code with labels supplied
  Sub $register, 1;                                                             # Set flags
  Jnz $start;                                                                   # Continue if count still greater than zero
  SetLabel $end;                                                                # End of block
 }

sub ForEver(&)                                                                  # Iterate for ever.
 {my ($block) = @_;                                                             # Block to iterate

  my $start = Label;                                                            # Start label
  my $end   = Label;                                                            # End label

  SetLabel $start;                                                              # Start of loop

  &$block($start, $end);                                                        # End of loop

  Jmp $start;                                                                   # Restart loop
  SetLabel $end;                                                                # End of loop
 }

#D2 Subroutine                                                                   # Create and call subroutines with the option of placing them into an area that can be writtento a file and reloaded and executed by another process.

my @VariableStack = (1);                                                        # Counts the number of parameters and variables on the stack in each invocation of L<Subroutine>.  There is at least one variable - the first holds the traceback.

sub SubroutineStartStack()                                                      #P Initialize a new stack frame.  The first quad of each frame has the address of the name of the sub in the low dword, and the parameter count in the upper byte of the quad.  This field is all zeroes in the initial frame.
 {push @VariableStack, 1;                                                       # Counts the number of variables on the stack in each invocation of L<Subroutine>.  The first quad provides the traceback.
 }

sub copyStructureMinusVariables($)                                              #P Copy a non recursive structure ignoring variables.
 {my ($s) = @_;                                                                 # Structure to copy

  my %s = %$s;
  for my $k(sort keys %s)                                                       # Look for sub structures
   {if (my $r = ref($s{$k}))
     {$s{$k} = __SUB__->($s{$k}) unless $r =~ m(\AVariable\Z);                  # We do not want to copy the variables yet because we are going to make them into references.
     }
   }

  bless \%s, ref $s;                                                            # Return a copy of the structure
 }

sub Subroutine(&%)                                                              # Create a subroutine that can be called in assembler code.
 {my ($block, %options) = @_;                                                   # Block of code as a sub, options
  my $export     = $options{export};                                            # File to export this subroutine to and all its contained subroutines
  my $name       = $options{name};                                              # Subroutine name
  my $parameters = $options{parameters};                                        # Parameters block
  my $structures = $options{structures};                                        # Structures provided as parameters
  my $trace      = $options{trace};                                             # If we are tracing and this subroutine is marked as traceable we always generate a new version of it so that we can trace each specific instance to get the exact context in which this subroutine was called rather than the context in which the original copy was called.

  if (1)                                                                        # Validate options
   {my %o = %options;
    delete $o{$_} for qw(export name parameters structures trace);
    if (my @i = sort keys %o)
     {confess "Invalid parameters: ".join(', ',@i);
     }
   }

  my %subroutinesSaved;                                                         # Current set of subroutine definitions
  my %rodataSaved;                                                              # Current set of read only elements
  my %rodatasSaved;                                                             # Current set of read only strings

  if ($export)                                                                  # Create a new set of subroutines for this routine and all of its sub routines
   {%subroutinesSaved = %subroutines;                                           # Save current set of subroutines
    %subroutines      = ();                                                     # New set of subroutines
    %rodataSaved      = %rodata;                                                # Current set of read only elements
    %rodata           = ();                                                     # New set of read only elements
    %rodatasSaved     = %rodatas;                                               # Current set of read only strings
    %rodatas          = ();                                                     # New set of read only strings
    $LibraryMode      = 1;                                                      # Please do not try to create a library while creating another library - create them one after the other in separate processes.
   }

# $name or confess "Name required for subroutine, use name=>";
  if ($name and my $s = $subroutines{$name})                                    # Return the label of a pre-existing copy of the code possibly after running the subroutine. Make sure that the subroutine name is different for different subs as otherwise the unexpected results occur.
   {return $s unless $TraceMode and $trace and !$export;                        # If we are tracing and this subroutine is marked as traceable we always generate a new version of it so that we can trace each specific instance to get the exact context in which this subroutine was called rather than the context in which the original copy was called.
   }

  if (1)                                                                        # Check for duplicate parameters
   {my %c;
    $c{$_}++ && confess "Duplicate parameter $_" for @$parameters;
   }

  SubroutineStartStack;                                                         # Open new stack layout with references to parameters
  my %parameters = map {$_ => R($_)} @$parameters;                              # Create a reference for each parameter.

  my %structureCopies;                                                          # Copies of the structures being passed that can be use inside the subroutine to access their variables in the stack frame of the subroutine
  if ($structures)                                                              # Structure provided in the parameter list
   {for my $n(sort keys %$structures)                                           # Each structure passed
     {$structureCopies{$n} = copyStructureMinusVariables($$structures{$n})      # A new copy of the structure with its variables left in place
     }
   }

  my $end   =    Label; Jmp $end;                                               # End label.  Subroutines are only ever called - they are not executed in-line so we jump over the implementation of the subroutine.  This can cause several forward jumps in a row if a number of subroutines are defined together.
  my $start = SetLabel;                                                         # Start label

  my $s = genHash(__PACKAGE__."::Subroutine",                                   # Subroutine definition. If we are creating a library the outer sub is also included in the library.
    block              => $block,                                               # Block used to generate this subroutine
    end                => $end,                                                 # End label for this subroutine
    export             => $export,                                              # File this subroutine was exported to if any
    name               => $name,                                                # Name of the subroutine from which the entry label is located
    offset             => undef,                                                # The offset of this routine in its library if it is in a library
    options            => \%options,                                            # Options used by the author of the subroutine
    parameters         => $parameters,                                          # Parameters definitions supplied by the author of the subroutine which get mapped in to parameter variables.
    start              => $start,                                               # Start label for this subroutine which includes the enter instruction used to create a new stack frame
    structureCopies    => \%structureCopies,                                    # Copies of the structures passed to this subroutine with their variables replaced with references
    structureVariables => {},                                                   # Map structure variables to references at known positions in the sub
    variables          => {%parameters},                                        # Map parameters to references at known positions in the sub
    vars               => $VariableStack[-1],                                   # Number of variables in subroutine
   );

  $subroutines{$name} = $s if $name;

  $s->mapStructureVariables(\%structureCopies) if $structures;                  # Map structures before we generate code so that we can put the parameters first in the new stack frame
  my $E = @text;                                                                # Code entry that will contain the Enter instruction

  Enter 0, 0;                                                                   # The Enter instruction is 4 bytes long
  &$block({%parameters}, {%structureCopies}, $s);                               # Code with parameters and structures

  my $V = pop @VariableStack;                                                   # Number of variables in subroutine stack frame. As parameters and structures are mapped into variables in the subroutine stack frame these variables will be included in the count as well.
     $V += RegisterSize(zmm0) / RegisterSize rax;                               # Ensures that we can save the parameter list using a full zmm register without the necessity o loading a mask register
  Leave if $V;                                                                  # Remove frame if there was one
  Ret;                                                                          # Return from the sub
  SetLabel $end;                                                                # The end point of the sub where we return to normal code
  my $w = RegisterSize rax;
  $text[$E] = $V ? <<END : '';                                                  # Rewrite enter instruction now that we know how much stack space, in bytes, that we need
  Enter $V*$w, 0
END

  if ($export)                                                                  # Create a new set of subroutines for this routine and all of its sub routines
   {$s->writeLibraryToArea({%subroutines});                                     # Place the subroutine in an area then write the area containing the subroutine and its contained routines to a file
    %subroutines = %subroutinesSaved;                                           # Save current set of subroutines
    %rodata      = %rodataSaved;                                                # Restore current set of read only elements
    %rodatas     = %rodatasSaved;                                               # Restore current set of read only strings
    $subroutines{$name} = $s if $name;                                          # Save current subroutine so we do not regenerate it
    $LibraryMode = 0;                                                           # Please do not try to create a library while creating another library - create them one after the other.
   }

  $s                                                                            # Return subroutine definition
 }

sub Nasm::X86::Subroutine::writeLibraryToArea($$)                               #P Write a subroutine library to an area then save the area in a file so that the subroutine can be reloaded at a later date either as separate file or via incorporation into a thing.  A thing was originally an assembly of people as in "The Allthing" or the "Stort Thing".
 {my ($s, $subs) = @_;                                                          # Sub definition of containing subroutine, definitions of contained subroutines
  my $a = CreateArea;
  my $address = K address => $s->start;
  my $size    = K size    => "$$s{end}-$$s{start}";
  my $off     = $a->appendMemory($address, $size);                              # Copy the containing subroutine into the area

  my %offsets; my %saved;                                                       # Contained subroutine name to offset mapping. Contained subroutine definitions
  for my $sub(sort keys %$subs)                                                 # Each sub routine definition contained in the containing subroutine
   {my $r = $$subs{$sub};                                                       # A routine within the subroutine
    $offsets{$r->name} = $off + K delta => "$$r{start}-$$s{start}";             # Offset to this sub routine within the subroutine
    $saved  {$r->name} = $$subs{$sub};                                          # Saved this subroutine definition as the sub is included in the area. The subroutine definition enables us to format the parameter list to the subroutine and so call it correctly.
   }

  my $h = $a->writeLibraryHeader({%offsets});                                   # Save the library header which tells us where each routine is and what it is called

  my $y = $a->yggdrasil;
  $y->put(&Nasm::X86::Yggdrasil::SubroutineOffsets, $h);                        # Record the location of the library header

  if (1)                                                                        # Save the definitions of the subs in this area
   {my $s = "SubroutineDefinitions:".dump(\%saved)."ZZZZ";                      # String to save
    my $d = $a->appendMemory(constantString($s));                               # Offset of string containing subroutine definition
    $y->put(&Nasm::X86::Yggdrasil::SubroutineDefinitions, $d);                  # Record the offset of the subroutine definition under the unique string number for this subroutine
   }

  $a->write(V file => Rs $s->export);                                           # Save the area to the named file
  $a->free;
 }

sub Nasm::X86::Area::writeLibraryHeader($$)                                     # Load a hash of subroutine names and offsets into an area
 {my ($area, $subs) = @_;                                                       # area to load into, hash of subroutine names to offsets
  my $w = RegisterSize(rax);

  my $a = $area;
  my %s = %$subs;
  my $l = $w * (1 + 2 * scalar keys %s);                                        # Number of quads required

  if (1)                                                                        # Length of concatenated names
   {use bytes;
    $l += length $_ for sort keys %s;
   }
  $l = int(($l + 8) / 8) * 8;                                                   # Enough room in bytes for quads and names

  Sub rsp, $l;                                                                  # make space in stack
  my $p = 0;
  Mov "qword[rsp+$p]", scalar keys %s; $p += $w;                                # Number of subroutines

  for my $s(sort keys %s)                                                       # Subroutine offsets and name lengths
   {use bytes;

    my $o = sub                                                                 # Either a variable or a constant
     {my $O = $s{$s};                                                           # Subroutine offset
      return $O unless ref($O);                                                 # Constant for easier testing
      $O->setReg(rsi);                                                          # Variable - place value in a free register
      rsi
     }->();

    Mov "qword[rsp+$p]", $o;        $p += $w;                                   # Subroutine offsets
    Mov "qword[rsp+$p]", length $s; $p += $w;                                   # Subroutine name lengths
   }

  for my $s(sort keys %s)                                                       # Subroutine names as one long string
   {use bytes;
    for my $i(1..length $s)
     {Mov "byte[rsp+$p]", ord substr($s, $i-1, 1); $p += 1;                     # Load each byte of the names
     }
   }

  my $y = $a->yggdrasil;                                                        # Establish Yggdrasil
  my $o = $a->appendMemory(V(address => rsp), V size => $l);                    # Load stack into area
  $y->put(&Nasm::X86::Yggdrasil::SubroutineOffsets, $o);                        # Save subroutine offsets

  Add rsp, $l;                                                                  # Restores stack

  $o                                                                            # Return the offset of the library header in the area
 }

sub Nasm::X86::Area::readLibraryHeader($$;$)                                    #P Create a tree mapping the numbers assigned to subroutine names to the offsets of the corresponding routines in a library returning the intersection so formed mapping the lexical item numbers (not names) encountered during parsing with the matching routines in the library. Optionally a subroutine (like Nasm::X86::Unisyn::Lex::letterToNumber) can be provided that returns details of an array that maps a single utf32 character to a smaller number which will be assumed to be the number of the routine with that single letter as its name.
 {my ($area, $uniqueStrings, $singleLetterArray) = @_;                          # Area containing subroutine library, unique strings from parse, subroutine returning details of a single character mapping array
  @_ >= 2 or confess "At least two parameters";

  my $a = $area;
  my $u = $uniqueStrings;                                                       # Unique strings from parse
  my $y = $a->yggdrasil;                                                        # Establish Yggdrasil
  $y->find(&Nasm::X86::Yggdrasil::SubroutineOffsets);                           # Find subroutine offsets

  If $y->found == 0,                                                            # If there are no subroutine offsets then this is not a library
  Then
   {&PrintErrTraceBack("Not a library");
   };

  PushR my $count = r15, my $sub = r14, my $name = r13, my $library = r12;      # Number of subroutines, offset and name length block, name address
  my $w = RegisterSize $count;
  ($a->address + $y->data)->setReg($library);
  Mov $count, "[$library]";                                                     # The number of subroutines
  Lea $sub,   "[$library+$w]";                                                  # Address of offset, name length array
  Add $library, $w;                                                             # Skip count field
  Mov $name, $count;
  Shl $name, 4;                                                                 # Size of offset, name block
  Add $name, $library;                                                          # Address in library of first name - they are all concatenated together one after the other

  my $A = CreateArea;                                                           # Area to contain mapping
  my $t = $A->CreateTree;                                                       # Resulting mapping
  my $s = $A->CreateTree(stringTree=>1);                                        # String keys tree mapping routine names to offsets

  ToZero                                                                        # Each subroutine
   {$s->putKeyString(                                                           # Routine name to subroutine offset mapping
            (my $n = V address => $name),
            (my $l = V length  => "[$sub+$w]"),
             my $o = V offset  =>"[$sub]");

    if ($singleLetterArray)                                                     # Include single character routines if they are in the optional mapping array
     {my ($char, $size, $fail) = &GetNextUtf8CharAsUtf32($n);                   # Get the next UTF-8 encoded character from the addressed memory and return it as a UTF-32 char.

      If $l == $size,                                                           # Did we read all of the name as one unicode character despite the fact that it occupied several bytes?
      Then                                                                      # Single character name
       {my ($n, $l) = &$singleLetterArray;
        If $char < $n,
        Then                                                                    # The single character is in the domain of the mapping array
         {$l->setReg(rax);                                                      # Address array of single letters
          $char->setReg(rsi);                                                   # The character we want to test as utf32
          Mov edx, "[rax+4*rsi]";                                               # The smaller number representing the letter
          Cmp edx, -1;
          IfNe
          Then                                                                  # The found smaller number is valid
           {Neg rdx;                                                            # Count down from zero rather then up to avoid immediate collisions with routines with multiple letters in their names
            $t->put(V(key => rdx), V offset => "[$sub]");                       # Access routine via a single character operator name
           };
         };
       };
     }

    $u->getKeyString(V(address => $name), V(length => "[$sub+$w]"));            # Check whether the subroutine name matches any string in the key string tree.
    If $u->found > 0,
    Then                                                                        # Subroutine name matches a unique string so record the mapping between string number and subroutine offset
     {$t->put($u->data, V offset => "[$sub]");
     };
    Add $name, "[$sub+$w]";
    Add $sub, 2 * $w;
   } $count;

  PopR;

 ($t, $s)                                                                       # Tree mapping subroutine assigned numbers to subroutine offsets in the library area
 }

sub Nasm::X86::Subroutine::subroutineDefinition($$$)                            #P Get the definition of a subroutine from an area.
 {my ($area, $file, $name) = @_;                                                # Area - but only to get easy access to this routine, file containing area, name of subroutine whose details we want
  my $a = readBinaryFile $file;                                                 # Reload the area
  my $b = $a =~ m(SubroutineDefinitions:(.*)ZZZZ)s ? $1 : '';                   # Extract Perl subroutine definition code from area as a string
  my $c = eval $b;                                                              # Convert to Perl data structure
  confess "Cannot extract subroutine definition from file $file\n$@\n" if $@;   # Complain if the eval failed
  $$c{a};                                                                       # Extract subroutine definition
 }

sub Nasm::X86::Subroutine::mapStructureVariables($$@)                           #P Find the paths to variables in the copies of the structures passed as parameters and replace those variables with references so that in the subroutine we can refer to these variables regardless of where they are actually defined.
 {my ($sub, $S, @P) = @_;                                                       # Sub definition, copies of source structures, path through copies of source structures to a variable that becomes a reference
  for my $s(sort keys %$S)                                                      # Source keys
   {my $e = $$S{$s};
    my $r = ref $e;
    next unless $r;

    if ($r =~ m(Variable)i)                                                     # Replace a variable with a reference in the copy of a structure passed in as a parameter
     {push @P, $s;
      my $R = $sub->structureVariables->{dump([@P])} = $$S{$s} = R($e->name);   # Path to a reference in the copy of a structure passed as as a parameter
      pop @P;
     }
    else                                                                        # A reference to something else - for the moment we assume that structures are built from non recursive hash references
     {push @P, $s;                                                              # Extend path
      $sub->mapStructureVariables($e, @P);                                      # Map structure variable
      pop @P;
     }
   }
 }

sub Nasm::X86::Subroutine::uploadStructureVariablesToNewStackFrame($$$@)        #P Create references to variables in parameter structures from variables in the stack frame of the subroutine.
 {my ($sub, $sv, $S, @P) = @_;                                                  # Sub definition, Structure variables, Source tree of input structures, path through source structures tree

  for my $s(sort keys %$S)                                                      # Source keys
   {my $e = $$S{$s};
    my $r = ref $e;
    next unless $r;                                                             # Element in structure is not a variable or another hash describing a sub structure
    if ($r =~ m(Variable)i)                                                     # Variable located
     {push @P, $s;                                                              # Extend path
      my $p = dump([@P]);                                                       # Path as string
      my $R = $sub->structureVariables->{$p};                                   # Reference
      if (defined($R))
       {$sub->uploadToNewStackFrame($sv, $e, $R);                               # Reference to structure variable from subroutine stack frame
       }
      else                                                                      # Unable to locate the corresponding reference
       {confess "No entry for $p in structure variables";
       }
      pop @P;
     }
    else                                                                        # A hash that is not a variable and is therefore assumed to be a non recursive substructure
     {push @P, $s;
      $sub->uploadStructureVariablesToNewStackFrame($sv, $e, @P);
      pop @P;
     }
   }
 }

sub Nasm::X86::Subroutine::uploadToNewStackFrame($$$$)                          #P Map a variable in the current stack into a reference in the next stack frame being the one that will be used by this sub.
 {my ($sub, $sv, $source, $target) = @_;                                        # Subroutine descriptor, structure variables, source variable in the current stack frame, the reference in the new stack frame
  @_ == 4 or confess "Four parameters";

  my $label = $source->label;                                                   # Source in current frame

  if ($source->reference)                                                       # Source is a reference
   {Mov r15, "[$label]";
    push @$sv, [$source, $target, 1];                                           # Source to target mapping and target is a reference
   }
  else                                                                          # Source is not a reference
   {Lea r15, "[$label]";
    push @$sv, [$source, $target, 0];                                           # Source to target mapping and target is not a reference
   }

  my $q = $target->label;
     $q =~ s(rbp) (rsp);                                                        # Labels are based off the stack frame but we are building a new stack frame here so we must rename the stack pointer.
  my $w = RegisterSize r15;
  Mov "[$q-$w*2]", r15;                                                         # Step over subroutine name pointer and previous frame pointer.
 }

sub Nasm::X86::Subroutine::validateParameters($%)                               #P Check that the parameters and structures presented in a call to a subroutine math those defined for the subroutine.
 {my ($sub, %options) = @_;                                                     # Subroutine descriptor, options

  my %o = %options;                                                             # Validate options
  delete $o{$_} for qw(parameters structures override library);                 # Parameters are variables, structures are Perl data structures with variables embedded in them,  override is a variable that contains the actual address of the subroutine

  if (my @i = sort keys %o)
   {confess "Invalid parameters: ".join(', ',@i);
   }

  my $parameters = $options{parameters};                                        # Parameters hash
  !$parameters or ref($parameters) =~ m(hash)i or confess
    "Parameters must be formatted as a hash";

  my $structures = $options{structures};                                        # Structures hash
  !$structures or ref($structures) =~ m(hash)i or confess
    "Structures must be formatted as a hash";

  if ($parameters)                                                              # Check for invalid or missing parameters
   {my %p = map {$_=>1} $sub->parameters->@*;
    my @m;
    for my $p(sort keys %$parameters)
     {push @m, "Invalid parameter: '$p'" unless $p{$p};
     }
    for my $p(sort keys %p)
     {push @m, "Missing parameter: '$p'" unless defined $$parameters{$p};
     }
    if (@m)
     {push @m, "Valid parameters : ";
           $m[-1] .= join ", ", map {"'$_'"} sort $sub->parameters->@*;
      confess join '', map {"$_\n"} @m;
     }
   }
  elsif ($sub->parameters->@*)                                                  # Complain about a lack of parameters if parameters have been defined for this subroutine
   {confess "Parameters required";
   }

  if ($structures)                                                              # Check for invalid or missing structures
   {my %s = $sub->options->{structures}->%*;
    my @m;
    for my $s(sort keys %$structures)
     {push @m, "Invalid structure: '$s'" unless $s{$s};
     }
    for my $s(sort keys %s)
     {push @m, "Missing structure: '$s'" unless $$structures{$s};
     }
    if (@m)
     {push @m, "Valid structures : ";
           $m[-1] .= join ", ", map {"'$_'"} sort keys %s;
      confess join '', map {"$_\n"} @m;
     }
   }
  elsif ($sub->options and                                                      # Complain about a lack of structures if structures have been defined for this subroutine
         $sub->options->{structures} and $sub->options->{structures}->%*)
   {confess "Structures required";
   }

  ($parameters, $structures)
 }

sub Nasm::X86::Subroutine::call($%)                                             # Call a sub optionally passing it parameters.
 {my ($sub, %options) = @_;                                                     # Subroutine descriptor, options

  my ($parameters, $structures) = $sub->validateParameters(%options);           # Validate the supplied parameters and structures against the specification defining this subroutine

  my $new = sub                                                                 # Regenerate the subroutine if we are tracing in general and this subroutine is specifically traceable.  We do not trace all subroutines because the generated asm code would be big.
   {if ($sub->options->{trace} and $TraceMode)                                  # Call the latest version of this subroutine not the original version in case the latest version fails so we can see the exact call stack of the latest version than the call stack of the original version in the context in which it was originally called.
     {return Subroutine(sub{$$sub{block}->&*}, $sub->options->%*);
     }
    undef
   }->();

  my $w = RegisterSize r15;                                                     # Size of a parameter
  PushR 15;                                                                     # Use this register to transfer between the current frame and the next frame
  Mov "dword[rsp  -$w*3]", Rs($sub->name // 'anon');                            # Point to subroutine name
  Mov "byte [rsp-1-$w*2]", $sub->vars;                                          # Number of parameters to enable trace back with parameters

  for my $name(sort keys $parameters->%*)                                       # Upload the variables referenced by the parameters to the new stack frame
   {my $s = $$parameters{$name};
    my $t = $sub->variables->{$name};
    $sub->uploadToNewStackFrame(my $structureVariables = [], $s, $t);
   }

  my $name = $$sub{name};                                                       # The name of the sub

  if ($structures)                                                              # Upload the variables of each referenced structure to the new stack frame
   {push @text, <<END;                                                          # A comment we can reverse up to if we decide to use a zmm to transfer the parameters
;AAAAAAAA
END
    $sub->uploadStructureVariablesToNewStackFrame
     (my $structureVariables = [], $structures);
                                                                                # Use zmm registers, if possible, to reduce the number of instructions required to zap the parameter list
    my %st; my $nr = 0; my $nd = 0;                                             # Target to source mapping for parameters. Number of references, number of direct parameters.
    for my $v(@$structureVariables)                                             # Each source to target pair
     {my $s = $$v[0]->position;                                                 # Source position in old stack frame
      my $r = $$v[0]->reference ? 1 : 0;                                        # Reference parameter or direct parameter
      my $t = $$v[1]->position;                                                 # Target position in the new stack frame
      my $k = sprintf "%08d", $t;                                               # Normalize the target position so it can be sorted
      $st{$k} = $s;                                                             # Target from source mapping
      $r ? ++$nr : ++$nd;                                                       # Number of references versus number of direct parameters
     }

    my @st = map{[$_+0, $st{$_}]} sort keys %st;                                # Parameters in stack frame order

    if (1 and (!$nr && $nd or $nr && !$nd) and                                  # The mapping is compact so we can do the whole thing without masking - and - the mapping is big enough to use zmm registers.  Further we are either doing everything by reference or everything directly - we do not have a mixture of references and directs require more instructions to handle - the goal here is, after all, to reduce the number of instructions required to construct a parameter list.
        @st >= 4 and 1 + $st[-1][0] - $st[0][0] == @st)
     {pop @text while @text and $text[-1] !~ m(\;AAAAAAAA);                     # Back up to the start of the structure parameters
      my $w = RegisterSize rax;                                                 # Size of one parameter
      my $W = RegisterSize zmm0;                                                # Space in parameter block
      my $b = $W / $w;                                                          # Number of parameters per block
      my @o;                                                                    # The offsets to load into one zmm register at a time to zap the parameter list.

      Vpbroadcastq zmm0, rbp if $nd;                                            # Direct parameters: Load the value of the stack base pointer into every cell to compute the address of each source parameter

      my $stackOffsetForParameterBlock = 1 + $st[0][0];                         # We start to load the parameters into the new stack (first) at this location
      for my $i(keys @st)                                                       # Each source to target mapping
       {push @o, $st[$i][1];                                                    # Offset of source
        if (@o == $b or $i == $#st)                                             # Dump the latest block of parameters
         {push @o, 0 while @o < $b;                                             # Pad to a full block
          my $o = Rq map {$w * -$_} reverse @o;                                 # Offsets for this zap block
          if ($nd)                                                              # All direct parameters
           {Vpaddq zmm1, zmm0, "[$o]";                                          # Add the offsets of the base of the stack frame to get the address of each parameter
           }
          else                                                                  # All direct parameters
           {Vmovdqu8 zmm0, qq([$o]);                                            # Load offsets from zap table
            Kxnorq k1, k1, k1;                                                  # Reference parameters: Set mask to all ones - we can safely load offsets of zero as they will simply load the value of rbp. Mask register set to zero at all bits where it loaded successfully.
            Vpgatherqq zmmM(1, 1), "[rbp+zmm0]";                                # Load the contents of memory at these offsets from rbp
           }
          my $p = $w * $stackOffsetForParameterBlock + $W;                      # Offset at which we start the layout of the latest parameter block
          Vmovdqu8 qq([rsp-$p]), zmm1;                                          # Layout the parameter zap table
          $stackOffsetForParameterBlock += $b;                                  # Next block of parameters
          @o = ();                                                              # Clear parameter block to accept new parameters
         }
       }
     }
   }

  if (my $l = $options{library})                                                # A variable containing the start address of a library
   {$l->setReg(rdi);
    if (my $o = $sub->offset)                                                   # The offset of the entry point into the library
     {$o->setReg(rsi);
      Add rdi, rsi;
      Call rdi;
     }
    else
     {confess "Supply the address of the containing library";
     }
   }
  elsif (my $o = $options{override})                                            # A variable containing the address of the subroutine to call
   {#$o->setReg(rdi);
    #Call rdi;
    Call $o->addressExpr;
   }
  else                                                                          # Call via label
   {if ($new)                                                                   # Call new generation created for tracing
     {Call $new->start;
     }
    else                                                                        # Call original generation
     {Call $sub->start;
     }
   }
  PopR;
 }

sub Nasm::X86::Subroutine::inline($%)                                           # Call a sub by in-lining it, optionally passing it parameters.
 {my ($sub, %options) = @_;                                                     # Subroutine descriptor, options

  my ($parameters, $structures) = $sub->validateParameters(%options);           # Validate the supplied parameters and structures against the specification defining this subroutine

  $sub->block->($parameters, $structures, $sub);                                # Generate code using the supplied parameters and structures
 }

#D2 Trace back                                                                  # Generate a subroutine calll trace back

sub PrintTraceBack($)                                                           #P Trace the call stack.
 {my ($channel) = @_;                                                           # Channel to write on

  my $s = Subroutine
   {PushR my @save = (rax, rdi, r9, r10, r8, r12, r13, r14, r15);
    my $stack     = r15;
    my $count     = r14;
    my $index     = r13;
    my $parameter = r12;                                                        # Number of parameters
    my $maxCount  = r8;                                                         # Maximum number of parameters - should be r11 but r11 is free in linux and does not survive syscalls.
    my $depth     = r10;                                                        # Depth of trace back
    ClearRegisters @save;

    Mov $stack, rbp;                                                            # Current stack frame
    AndBlock                                                                    # Each level
     {my ($fail, $end, $start) = @_;                                            # Fail block, end of fail block, start of test block
      Mov $stack, "[$stack]";                                                   # Up one level
      Mov rax, "[$stack-8]";
      Mov $count, rax;
      Shr $count, 56;                                                           # Top byte contains the parameter count
      Cmp $count, $maxCount;                                                    # Compare this count with maximum so far
      Cmovg $maxCount, $count;                                                  # Update count if greater
      Shl rax, 8; Shr rax, 8;                                                   # Remove parameter count
      Je $end;                                                                  # Reached top of stack if rax is zero
      Inc $depth;                                                               # Depth of trace back
      Jmp $start;                                                               # Next level
     };

    Mov $stack, rbp;                                                            # Current stack frame
    &PrintNL($channel);                                                         # Print title
    &PrintString($channel, "Subroutine trace back, depth: ");
    PushR rax;
    Mov rax, $depth;
    &PrintRaxRightInDec(V(width=>2), $channel);
    PopR rax;
    &PrintNL($channel);

    AndBlock                                                                    # Each level
     {my ($fail, $end, $start) = @_;                                            # Fail block, end of fail block, start of test block
      Mov $stack, "[$stack]";                                                   # Up one level
      Mov rax, "[$stack-8]";
      Mov $count, rax;
      Shr $count, 56;                                                           # Top byte contains the parameter count
      Shl rax, 8; Shr rax, 8;                                                   # Remove parameter count
      Je $end;                                                                  # Reached top of stack
      Cmp $count, 0;                                                            # Check for parameters
      IfGt
      Then                                                                      # One or more parameters
       {Mov $index, 0;
        For
         {my ($start, $end, $next) = @_;
          Mov $parameter, $index;
          Add $parameter, 2;                                                    # Skip traceback
          Shl $parameter, 3;                                                    # Each parameter is a quad
          Neg $parameter;                                                       # Offset from stack
          Add $parameter, $stack;                                               # Position on stack
          Mov $parameter, "[$parameter]";                                       # Parameter reference to variable
          Push rax;
          Mov rax, "[$parameter]";                                              # Variable content
          &PrintRaxInHex($channel);
          Pop rax;
          &PrintSpace($channel, 4);
         } $index, $count;
        For                                                                     # Vertically align subroutine names
         {my ($start, $end, $next) = @_;
          &PrintSpace($channel, 23);
         } $index, $maxCount;
       };

      StringLength(&V(string => rax))->setReg(rdi);                             # Length of name of subroutine
      &PrintMemoryNL($channel);                                                 # Print name of subroutine
      Jmp $start;                                                               # Next level
     };
    &PrintNL($channel);
    PopR;
   } name => "SubroutineTraceBack_$channel";

  $s->call;
 }

sub PrintErrTraceBack($)                                                        #P Print sub routine track back on stderr and then exit with a message.
 {my ($message) = @_;                                                           # Reason why we are printing the trace back and then stopping
  my ($p, $f, $l) = caller(0);
  PrintStringNL($stderr, "$message at $0 line $l");
  PrintTraceBack($stderr);
  Exit(1);
 }

sub PrintOutTraceBack($)                                                        # Print sub routine track back on stdout and then exit with a message.
 {my ($message) = @_;                                                           # Reason why we are printing the trace back and then stopping
  &PrintOutStringNL($message);
  PrintTraceBack($stdout);
  Exit(1);
 }

sub OnSegv()                                                                    # Request a trace back followed by exit on a B<segv> signal.
 {my $s = Subroutine                                                            # Subroutine that will cause an error to occur to force a trace back to be printed
   {my $end = Label;
    Jmp $end;                                                                   # Jump over subroutine definition
    my $start = SetLabel;
    Enter 0, 0;                                                                 # Inline code of signal handler
    Mov r15, rbp;                                                               # Preserve the new stack frame
    Mov rbp, "[rbp]";                                                           # Restore our last stack frame
    PrintOutTraceBack 'Segmentation error';                                     # Print our trace back
    Mov rbp, r15;                                                               # Restore supplied stack frame
    Exit(0);                                                                    # Exit so we do not trampoline. Exit with code zero to show that the program is functioning correctly, else L<Assemble> will report an error.
    Leave;
    Ret;
    SetLabel $end;

    Mov r15, 0;                                                                 # Push sufficient zeros onto the stack to make a structure B<sigaction> as described in: https://www.man7.org/linux/man-pages/man2/sigaction.2.html
    Push r15 for 1..16;

    Mov r15, $start;                                                            # Actual signal handler
    Mov "[rsp]", r15;                                                           # Show as signal handler
    Mov "[rsp+0x10]", r15;                                                      # Add as trampoline as well - which is fine because we exit in the handler so this will never be called
    Mov r15, 0x4000000;                                                         # Mask to show we have a trampoline which is, apparently, required on x86
    Mov "[rsp+0x8]", r15;                                                       # Confirm we have a trampoline

    Mov rax, 13;                                                                # B<Sigaction> from B<kill -l>
    Mov rdi, 11;                                                                # Confirmed B<SIGSEGV = 11> from B<kill -l> and tracing with B<sde64>
    Mov rsi, rsp;                                                               # Structure B<sigaction> structure on stack
    Mov rdx, 0;                                                                 # Confirmed by trace
    Mov r10, 8;                                                                 # Found by tracing B<signal.c> with B<sde64> it is the width of the signal set and mask. B<signal.c> is reproduced below.
    Syscall;
    Add rsp, 128;
   } [], name=>"on segv";

  $s->call;
 }

#D2 Comments                                                                    # Inserts comments into the generated assember code.

sub Comment(@)                                                                  # Insert a comment into the assembly code.
 {my (@comment) = @_;                                                           # Text of comment
  my $c = join "", @comment;
  my ($p, $f, $l) = caller;
  push @text, <<END;
; $c at $f line $l
END
 }

#D1 Print                                                                       # Print the values of registers and memory interspersed with constant strings.  The print commands do not overwrite the free registers as doing so would make debugging difficult.

#D2 Strings                                                                     # Print constant and variable strings

sub PrintNL($)                                                                  #P Print a new line to stdout  or stderr.
 {my ($channel) = @_;                                                           # Channel to write on
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {SaveFirstFour;
    Mov rax, 1;
    Mov rdi, $channel;                                                          # Write below stack
    my $w = RegisterSize rax;
    Lea  rsi, "[rsp-$w]";
    Mov "QWORD[rsi]", 10;
    Mov rdx, 1;
    Syscall;
    RestoreFirstFour
   } name => qq(PrintNL_$channel);

  $s->call;
 }

sub PrintErrNL()                                                                #P Print a new line to stderr.
 {@_ == 0 or confess;
  PrintNL($stderr);
 }

sub PrintOutNL()                                                                # Print a new line to stderr.
 {@_ == 0 or confess;
  PrintNL($stdout);
 }

sub PrintString($@)                                                             #P Print a constant string to the specified channel.
 {my ($channel, @string) = @_;                                                  # Channel, Strings
  @_ >= 2 or confess "Two or more parameters";

  my $c = join ' ', @string;
  my $l = length($c);
  my $a = Rs($c);

  my $s = Subroutine
   {SaveFirstFour;
    Mov rax, 1;
    Mov rdi, $channel;
    Lea rsi, "[$a]";
    Mov rdx, $l;
    Syscall;
    RestoreFirstFour;
   } name => "PrintString_${channel}_${c}";

  $s->call;
 }

sub PrintStringNL($@)                                                           #P Print a constant string to the specified channel followed by a new line.
 {my ($channel, @string) = @_;                                                  # Channel, Strings
  PrintString($channel, @string);
  PrintNL    ($channel);
 }

sub PrintErrString(@)                                                           #P Print a constant string to stderr.
 {my (@string) = @_;                                                            # String
  PrintString($stderr, @string);
 }

sub PrintErrStringNL(@)                                                         #P Print a constant string to stderr followed by a new line.
 {my (@string) = @_;                                                            # String
  PrintErrString(@string);
  my @c = caller 0;
  my (undef, $file, $line) = @c;
  PrintErrString "                                                              called at $file line $line";
  PrintErrNL;
 }

sub PrintOutString(@)                                                           # Print a constant string to stdout.
 {my (@string) = @_;                                                            # String
  PrintString($stdout, @string);
 }

sub PrintOutStringNL(@)                                                         # Print a constant string to stdout followed by a new line.
 {my (@string) = @_;                                                            # String
  PrintOutString(@string);
  PrintOutNL;
 }

sub PrintCString($$)                                                            #P Print a zero terminated C style string addressed by a variable on the specified channel.
 {my ($channel, $string) = @_;                                                  # Channel, String

  PushR rax, rdi;
  my $length = &StringLength($string);                                          # Length of string
  $string->setReg(rax);
  $length->setReg(rdi);
  &PrintOutMemory();                                                            # Print string
  PopR;
 }

sub PrintCStringNL($$)                                                          #P Print a zero terminated C style string addressed by a variable on the specified channel followed by a new line.
 {my ($channel, $string) = @_;                                                  # Channel, Strings
  PrintCString($channel, $string);
  PrintNL     ($channel);
 }

sub PrintSpace($;$)                                                             #P Print a constant number of spaces to the specified channel.
 {my ($channel, $spaces) = @_;                                                  # Channel, number of spaces if not one.
  PrintString($channel, ' ' x ($spaces // 1));
 }

sub PrintErrSpace(;$)                                                           #P Print  a constant number of spaces to stderr.
 {my ($spaces) = @_;                                                            # Number of spaces if not one.
  PrintErrString(' ', $spaces);
 }

sub PrintOutSpace(;$)                                                           # Print a constant number of spaces to stdout.
 {my ($spaces) = @_;                                                            # Number of spaces if not one.
  PrintOutString ' ' x ($spaces//1);
 }

#D2 Registers                                                                   # Print selected registers in a variety of formats.

sub hexTranslateTable                                                           #P Create/address a hex translate table and return its label.
 {my $h = '0123456789ABCDEF';
  my @t;
  for   my $i(split //, $h)
   {for my $j(split //, $h)
     {my $h = "$i$j";
         $h =~ s(\A00) (..);
         $h =~ s(\A0)  (.);
      push @t, $h;
     }
   }
   Rs @t                                                                        # Constant strings are only saved if they are unique, else a read only copy is returned.
 }

sub PrintRaxInHex($;$)                                                          #P Write the content of register rax in hexadecimal in big endian notation to the specified channel.
 {my ($channel, $end) = @_;                                                     # Channel, optional end byte
  @_ == 1 or @_ == 2 or confess "One or two parameters";
  my $hexTranslateTable = hexTranslateTable;
  $end //= 7;                                                                   # Default end byte

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $success = Label;                                                        # End of subroutine
    Cmp rax, 0;
    IfEq                                                                        # Rax is zero - special case
    Then
     {PrintString($channel, ".... .... .... ...0");
      Jmp $success;
     };
    Cmp rax, -1;
    IfEq                                                                        # Rax is minus one - special case
    Then
     {PrintString($channel, ".... .... .... ..-1");
      Jmp $success;
     };

    SaveFirstFour;                                                              # Rax is a parameter
    Mov rdx, rax;                                                               # Content to be printed
    Mov rdi, 2;                                                                 # Length of a byte in hex

    for my $i((7-$end)..7)                                                      # Each byte
     {my $s = $bitsInByte*$i;
      Mov rax, rdx;
      Shl rax, $s;                                                              # Push selected byte high
      Shr rax, (RegisterSize(rax) - 1) * $bitsInByte;                           # Push select byte low
      Shl rax, 1;                                                               # Multiply by two because each entry in the translation table is two bytes long
      Lea rsi, "[$hexTranslateTable]";
      Add rax, rsi;
      PrintMemory($channel);                                                    # Print memory addressed by rax for length specified by rdi
      PrintString($channel, ' ') if $i % 2 and $i < 7;
     }
    RestoreFirstFour;

    SetLabel $success;
   } name => "PrintOutRaxInHexOn-$channel-$end";

  $s->call;
 }

sub PrintErrRaxInHex()                                                          #P Write the content of register rax in hexadecimal in big endian notation to stderr.
 {@_ == 0 or confess;
  PrintRaxInHex($stderr);
 }

sub PrintErrRaxInHexNL()                                                        #P Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.
 {@_ == 0 or confess;
  PrintRaxInHex($stderr);
  PrintErrNL;
 }

sub PrintOutRaxInHex()                                                          # Write the content of register rax in hexadecimal in big endian notation to stout.
 {@_ == 0 or confess;
  PrintRaxInHex($stdout);
 }

sub PrintOutRaxInHexNL()                                                        # Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.
 {@_ == 0 or confess;
  PrintRaxInHex($stdout);
  PrintOutNL;
 }

sub PrintRax_InHex($;$)                                                         #P Write the content of register rax in hexadecimal in big endian notation to the specified channel replacing zero bytes with __.
 {my ($channel, $end) = @_;                                                     # Channel, optional end byte
  @_ == 1 or @_ == 2 or confess "One or two parameters";
  my $hexTranslateTable = hexTranslateTable;
  $end //= 7;                                                                   # Default end byte

  my $s = Subroutine
   {SaveFirstFour;                                                              # Rax is a parameter
    Mov rdx, rax;                                                               # Content to be printed
    Mov rdi, 2;                                                                 # Length of a byte in hex

    for my $i((7-$end)..7)                                                      # Each byte
     {my $s = $bitsInByte*$i;
      Mov rax, rdx;
      Shl rax, $s;                                                              # Push selected byte high
      Shr rax, (RegisterSize(rax) - 1) * $bitsInByte;                           # Push select byte low
      Cmp rax, 0;
      IfEq                                                                      # Print __ for zero bytes
      Then
       {PrintString($channel, "__");
       },
      Else                                                                      # Print byte in hexadecimal otherwise
       {Shl rax, 1;                                                             # Multiply by two because each entry in the translation table is two bytes long
        Lea rsi, "[$hexTranslateTable]";
        Add rax, rsi;
        PrintMemory($channel);                                                  # Print memory addressed by rax for length specified by rdi
       };
      PrintString($channel, ' ') if $i % 2 and $i < 7;
     }
    RestoreFirstFour;
   } name => "PrintOutRax_InHexOn-$channel-$end";

   $s->call;
 }

sub PrintErrRax_InHex()                                                         #P Write the content of register rax in hexadecimal in big endian notation to stderr.
 {@_ == 0 or confess;
  PrintRax_InHex($stderr);
 }

sub PrintErrRax_InHexNL()                                                       #P Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.
 {@_ == 0 or confess;
  PrintRax_InHex($stderr);
  PrintErrNL;
 }

sub PrintOutRax_InHex()                                                         # Write the content of register rax in hexadecimal in big endian notation to stout.
 {@_ == 0 or confess;
  PrintRax_InHex($stdout);
 }

sub PrintOutRax_InHexNL()                                                       # Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.
 {@_ == 0 or confess;
  PrintRax_InHex($stdout);
  PrintOutNL;
 }

sub PrintOutRaxInReverseInHex                                                   # Write the content of register rax to stderr in hexadecimal in little endian notation.
 {@_ == 0 or confess;
  Comment "Print Rax In Reverse In Hex";
  Push rax;
  Bswap rax;
  PrintOutRaxInHex;
  Pop rax;
 }

sub PrintOneRegisterInHex($$)                                                   #P Print the named register as a hex string.
 {my ($channel, $r) = @_;                                                       # Channel to print on, register to print
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {if   ($r =~ m(\Ar))                                                         # General purpose register
     {if ($r =~ m(\Arax\Z))                                                     # General purpose register - rax
       {PrintRaxInHex($channel);
       }
      else                                                                      # General purpose register not rax
       {PushR rax;
        Mov rax, $r;
        PrintRaxInHex($channel);
        PopR;
       }
     }
    elsif ($r =~ m(\Ak))                                                        # Mask register
     {PushR rax;
      Kmovq rax, $r;
      PrintRaxInHex($channel);
      PopR;
     }
    elsif ($r =~ m(\Axmm))                                                      # Xmm register
     {PushR rax, $r, 7;
      Mov rax, 0b10;
      Kmovq k7, rax;
      Vpcompressq "$r\{k7}", $r;
      Vmovq rax, $r;
      PrintRaxInHex($channel);
      PopR;

      PrintString($channel, "  ");                                              # Separate blocks of bytes with a space
      PushR rax;
      Vmovq rax, $r;
      PrintRaxInHex($channel);
      PopR;
     }
    elsif ($r =~ m(\Aymm))                                                      # Ymm register
     {PushR rax, $r, 7;
      Mov rax, 0b1100;
      Kmovq k7, rax;
      Vpcompressq "$r\{k7}", $r;
      PrintOneRegisterInHex($channel, $r =~ s(\Ay) (x)r);                       # Upper half
      PopR;

      PrintString($channel, " - ");                                             # Separate blocks of bytes with a space

      PrintOneRegisterInHex($channel, $r =~ s(\Ay) (x)r);                       # Lower half
     }
    elsif ($r =~ m(\Azmm))                                                      # Zmm register
     {PushR rax, $r, 7;
      Mov rax, 0b11110000;
      Kmovq k7, rax;
      Vpcompressq "$r\{k7}", $r;
      PrintOneRegisterInHex($channel, $r =~ s(\Az) (y)r);                       # Upper half
      PopR;

      PrintString($channel, " + ");                                             # Separate blocks of bytes with a space
      PrintOneRegisterInHex($channel, $r =~ s(\Az) (y)r);                       # Lower half
     }
   } name => "PrintOneRegister${r}InHexOn$channel";                             # One routine per register printed

  PushR $r;
  $s->call;
  PopR;
 }

sub PrintErrOneRegisterInHex($)                                                 #P Print the named register as a hex string on stderr.
 {my ($r) = @_;                                                                 # Register to print
  @_ == 1 or confess "One parameter";
  PrintOneRegisterInHex($stderr, $r)
 }

sub PrintErrOneRegisterInHexNL($)                                               #P Print the named register as a hex string on stderr followed by new line.
 {my ($r) = @_;                                                                 # Register to print
  @_ == 1 or confess "One parameter";
  PrintOneRegisterInHex($stderr, $r);
  PrintErrNL;
 }

sub PrintOutOneRegisterInHex($)                                                 # Print the named register as a hex string on stdout.
 {my ($r) = @_;                                                                 # Register to print
  @_ == 1 or confess "One parameter";
  PrintOneRegisterInHex($stdout, $r)
 }

sub PrintOutOneRegisterInHexNL($)                                               # Print the named register as a hex string on stdout followed by new line.
 {my ($r) = @_;                                                                 # Register to print
  @_ == 1 or confess "One parameter";
  PrintOneRegisterInHex($stdout, $r);
  PrintOutNL;
 }

sub PrintRegisterInHex($@)                                                      #P Print the named registers as hex strings.
 {my ($channel, @r) = @_;                                                       # Channel to print on, names of the registers to print
  @_ >= 2 or confess "Two or more parameters required";

  for my $r(map{registerNameFromNumber $_} @r)                                  # Each register to print
   {PrintString($channel,  sprintf("%6s: ", $r));                               # Register name
    PrintOneRegisterInHex $channel, $r;
    if ($channel == $stderr)                                                    # Print location in the source file in a format that Geany understands
     {my @c = caller 1;
      my (undef, $file, $line) = @c;
      PrintString $channel, "                                                   called at $file line $line";
     }
    PrintNL($channel);
   }
 }

sub PrintErrRegisterInHex(@)                                                    #P Print the named registers as hex strings on stderr.
 {my (@r) = @_;                                                                 # Names of the registers to print
  PrintRegisterInHex $stderr, @r;
 }

sub PrintOutRegisterInHex(@)                                                    # Print the named registers as hex strings on stdout.
 {my (@r) = @_;                                                                 # Names of the registers to print
  PrintRegisterInHex $stdout, @r;
 }

sub PrintOutRipInHex                                                            #P Print the instruction pointer in hex.
 {@_ == 0 or confess;
  my @regs = qw(rax);

  my $s = Subroutine
   {PushR @regs;
    my $l = Label;
    push @text, <<END;
$l:
END
    Lea rax, "[$l]";                                                            # Current instruction pointer
    PrintOutString "rip: ";
    PrintOutRaxInHex;
    PrintOutNL;
    PopR @regs;
   } name=> "PrintOutRipInHex";

  $s->call;
 }

sub PrintOutRflagsInHex                                                         #P Print the flags register in hex.
 {@_ == 0 or confess;
  my @regs = qw(rax);

  my $s = Subroutine
   {PushR @regs;
    Pushfq;
    Pop rax;
    PrintOutString "rfl: ";
    PrintOutRaxInHex;
    PrintOutNL;
    PopR @regs;
   } name=> "PrintOutRflagsInHex";

  $s->call;
 }

sub PrintOutRegistersInHex                                                      # Print the general purpose registers in hex.
 {@_ == 0 or confess "No parameters required";

  my $s = Subroutine
   {#PrintOutRipInHex;
    PrintOutRflagsInHex;

    my @regs = qw(rax);
    PushR @regs;

    my $w = registers_64();
    for my $r(sort @$w)
     {next if $r =~ m(rip|rflags|rbp|rsp);                                      # Modified by print routines so pointless to print
      if ($r eq rax)
       {Pop rax;
        Push rax
       }
      PrintOutString reverse(pad(reverse($r), 3)).": ";
      Mov rax, $r;
      PrintOutRaxInHex;
      PrintOutNL;
     }
    PopR @regs;
   } name=> "PrintOutRegistersInHex";

  $s->call;
 }

#D2 Zero Flag                                                                   # Print zero flag

sub PrintErrZF                                                                  #P Print the zero flag without disturbing it on stderr.
 {@_ == 0 or confess;

  Pushfq;
  IfNz Then {PrintErrStringNL "ZF=0"}, Else {PrintErrStringNL "ZF=1"};
  Popfq;
 }

sub PrintOutZF                                                                  # Print the zero flag without disturbing it on stdout.
 {@_ == 0 or confess "No parameters";

  Pushfq;
  IfNz Then {PrintOutStringNL "ZF=0"}, Else {PrintOutStringNL "ZF=1"};
  Popfq;
 }

#D2 Hexadecimal                                                                 # Print numbers in hexadecimal right justified in a field

sub PrintRightInHex($$$)                                                        #P Print out a number in hex right justified in a field of specified width on the specified channel.
 {my ($channel, $number, $width) = @_;                                          # Channel, number as a variable or register, width of output field as a constant

  $channel =~ m(\A(1|2)\Z) or confess "Invalid channel should be stderr or stdout";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    PushR rax, rdi, r14, r15, xmm0;
    ClearRegisters xmm0;
    $$p{number}->setReg(14);

    K(loop => 16)->for(sub
     {Mov r15, r14;                                                             # Load xmm0 with hexadecimal digits
      And r15, 0xf;
      Cmp r15, 9;
      IfGt
      Then
       {Add r15, ord('A') - 10;
       },
      Else
       {Add r15, ord('0');
       };
      Pslldq xmm0, 1;
      Pinsrb xmm0, r15b, 0;
      Shr r14, 4;
     });

    Block    ##IMPROVE                                                          # Translate leading zeros to spaces
     {my ($end) = @_;
      for my $i(0..14)
       {Pextrb r15, xmm0, $i;
        Cmp r15b, ord('0');
        Jne $end;
        Mov r15, ord(' ');
        Pinsrb xmm0, r15b, $i;
       }
     };

    PushR xmm0;                                                                 # Print xmm0 within the width of the field
    Mov rax, rsp;
    $$p{width}->setReg(rdi);
    And rdi, 0xff;                                                              # Limit field width to 16
    Add rax, 16;
    Sub rax, rdi;
    &PrintOutMemory();
    PopR;
    PopR;
   } name => "PrintRightInHex_${channel}",
     parameters=>[qw(width number)];

  $s->call(parameters =>
   {number => ref($number) ? $number : V(number => $number),
    width  => ref($width)  ? $width  : V width  => $width});
 }

sub PrintErrRightInHex($$)                                                      #P Write the specified variable in hexadecimal right justified in a field of specified width on stderr.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInHex($stderr, $number, $width);
 }

sub PrintErrRightInHexNL($$)                                                    #P Write the specified variable in hexadecimal right justified in a field of specified width on stderr followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInHex($stderr, $number, $width);
  PrintErrNL;
 }

sub PrintOutRightInHex($$)                                                      # Write the specified variable in hexadecimal right justified in a field of specified width on stdout.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInHex($stdout, $number, $width);
 }

sub PrintOutRightInHexNL($$)                                                    # Write the specified variable in hexadecimal right justified in a field of specified width on stdout followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInHex($stdout, $number, $width);
  PrintOutNL;
 }

#D2 Binary                                                                      # Print numbers in binary right justified in a field

sub PrintRightInBin($$$)                                                        #P Print out a number in binary right justified in a field of specified width on the specified channel.
 {my ($channel, $Number, $Width) = @_;                                          # Channel, number as a variable or register, width of output field as a variable or constant

  $channel =~ m(\A(1|2)\Z)     or confess "Channel should be stderr or stdout";
  my $number = ref($Number) ? $Number : V(number => $Number);                   # Variable or register
  my $width  = ref($Width)  ? $Width  : K(width  => ($Width//16));              # Promote constant

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    PushR rax, rdi, rsi, r14, r15;
    $$p{number}->setReg(rax);
    Mov rsi, rsp;
    my $bir = RegisterSize(rax) * $bitsInByte;
    Mov r14, rsi;
    Sub rsp, $bir;                                                              # Allocate space on the stack for the maximum length bit string written out as characters

    K(loop => $bir)->for(sub                                                    # Load bits onto stack as characters
     {Dec r14;
      Mov r15, rax;
      And r15, 1;
      Cmp r15, 0;
      IfNe
      Then
       {Mov "byte[r14]", ord('1');
       },
      Else
       {Mov "byte[r14]", ord('0');
       };
      Shr rax, 1;
     });

    K(loop => $bir)->for(sub                                                    # Replace leading zeros with spaces
     {my ($index, $start, $next, $end) = @_;
      Cmp "byte[r14]",ord('0');
      IfEq
      Then
       {Mov "byte[r14]", ord(' ');
       },
      Else
       {Jmp $end;
       };
      Inc r14;
     });

    Mov rax, rsp;                                                               # Write stack in a field of specified width
    $$p{width}->setReg(rdi);
    Add rax, $bir;
    Sub rax, rdi;
    PrintMemory($channel);
    Mov rsp, rsi;                                                               # Restore stack
    PopR;
   } name => "PrintRightInBin_${channel}",
     parameters=>[qw(width number)];

  $s->call(parameters => {number => $number, width=>$width});
 }

sub PrintErrRightInBin($$)                                                      #P Write the specified variable in binary right justified in a field of specified width on stderr.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInBin($stderr, $number, $width);
 }

sub PrintErrRightInBinNL($$)                                                    #P Write the specified variable in binary right justified in a field of specified width on stderr followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInBin($stderr, $number, $width);
  PrintErrNL;
 }

sub PrintOutRightInBin($$)                                                      # Write the specified variable in binary right justified in a field of specified width on stdout.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInBin($stdout, $number, $width);
 }

sub PrintOutRightInBinNL($;$)                                                   # Write the specified variable in binary right justified in a field of specified width on stdout followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable, width of output field as a variable
  PrintRightInBin $stdout, $number, ($width//16);
  PrintOutNL;
 }

#D2 Decimal                                                                     # Print numbers in decimal right justified in fields of specified width.

sub PrintRaxInDec($)                                                            #P Print rax in decimal on the specified channel.
 {my ($channel) = @_;                                                           # Channel to write on

  my $s = Subroutine
   {PushR rax, rdi, rdx, r9, r10;
    Mov r9, 0;                                                                  # Number of decimal digits
    Mov r10, 10;                                                                # Base of number system
    my $convert = SetLabel;
      Mov rdx, 0;                                                               # Rdx must be clear to receive remainder
      Idiv r10;                                                                 # Remainder after integer division by 10
      Add rdx, 48;                                                              # Convert remainder to ascii
      Push rdx;                                                                 # Save remainder
      Inc r9;                                                                   # Number of digits
      Cmp rax, 0;
    Jnz $convert;

    Mov rdi, 1;                                                                 # Length of each write

    my $print = SetLabel;                                                       # Print digits
      Mov rax, rsp;
      PrintMemory($channel);
      Dec r9;                                                                   # Number of digits
      Pop rax;                                                                  # Remove digit from stack
    Jnz $print;

    PopR;
   } name => "PrintRaxInDec_$channel";

  $s->call;
 }

sub PrintOutRaxInDec                                                            # Print rax in decimal on stdout.
 {PrintRaxInDec($stdout);
 }

sub PrintOutRaxInDecNL                                                          # Print rax in decimal on stdout followed by a new line.
 {PrintOutRaxInDec;
  PrintOutNL;
 }

sub PrintErrRaxInDec                                                            #P Print rax in decimal on stderr.
 {PrintRaxInDec($stderr);
 }

sub PrintErrRaxInDecNL                                                          #P Print rax in decimal on stderr followed by a new line.
 {PrintErrRaxInDec;
  PrintErrNL;
 }

sub PrintRaxRightInDec($$)                                                      #P Print rax in decimal right justified in a field of the specified width on the specified channel.
 {my ($Width, $channel) = @_;                                                   # Width as a variable or a constant, channel

  my $width  = ref($Width) ? $Width : K width => ($Width//16);                  # Promote constant

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    PushR rax, rdi, rdx, r9, r10;
    Mov r9, 0;                                                                  # Number of decimal digits
    Mov r10, 10;                                                                # Base of number system used to divide rax
    my $convert = SetLabel;
      Mov rdx, 0;                                                               # Rdx must be clear to receive remainder
      Idiv r10;                                                                 # Remainder after integer division of rax by 10
      Add rdx, 48;                                                              # Convert remainder to ascii
      Push rdx;                                                                 # Save remainder
      Inc r9;                                                                   # Number of digits
      Cmp rax, 0;
    Jnz $convert;

    Mov rdi, 1;                                                                 # Length of each write
    $$p{width}->setReg(10);                                                     # Pad to this width if necessary
    Cmp r9, r10;
    IfLt
    Then                                                                        # Padding required
     {(V(width => r10) - V(actual => r9))->spaces($channel);
     };

    my $print = SetLabel;                                                       # Print digits
      Mov rax, rsp;
      PrintMemory($channel);
      Dec r9;                                                                   # Number of digits
      Pop rax;                                                                  # Remove digit from stack
    Jnz $print;

    PopR;
   } parameters=>[qw(width)], name => "PrintRaxRightInDec_${channel}";

  $s->call(parameters=>{width => ref($width) ? $width : V width => $width});
 }

sub PrintRightInDec($$$)                                                        #P Print out a number in decimal right justified in a field of specified width on the specified channel.
 {my ($channel, $Number, $width) = @_;                                          # Channel, number as a variable or register, width of output field as a variable or constant

  my $number = ref($Number) ? $Number : V(number => $Number);                   # Variable or register

  PushR rax;
  $number->setReg(rax);
  PrintRaxRightInDec $width, $channel;
  PopR;
 }

sub PrintErrRightInDec($$)                                                      #P Print a variable or register in decimal right justified in a field of the specified width on stderr.
 {my ($number, $width) = @_;                                                    # Number as a variable or a register, width as a variable or constant
  PrintRightInDec($stderr, $number, $width);
 }

sub PrintErrRightInDecNL($$)                                                    #P Print a variable or register in decimal right justified in a field of the specified width on stderr followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable or a register, width as a variable or constant
  PrintErrRightInDec($number, $width);
  PrintErrNL;
 }

sub PrintOutRightInDec($$)                                                      # Print a variable or register in decimal right justified in a field of the specified width on stdout.
 {my ($number, $width) = @_;                                                    # Number as a variable or a register, width as a variable or constant
  PrintRightInDec($stdout, $number, $width);
 }

sub PrintOutRightInDecNL($$)                                                    # Print a variable or register in decimal right justified in a field of the specified width on stdout followed by a new line.
 {my ($number, $width) = @_;                                                    # Number as a variable or a register, width as a variable or constant
  PrintOutRightInDec($number, $width);
  PrintOutNL;
 }

#D2 Text                                                                        # Print the contents of a register as text.

sub PrintRaxAsText($)                                                           #P Print the string in rax on the specified channel.
 {my ($channel) = @_;                                                           # Channel to write on
  @_ == 1 or confess "One parameter";

  my $w = RegisterSize rax;
  PushR rdi, rdx, rax;
  Lzcnt rdi, rax;
  Shr rdi, 3;
  Mov rdx, rdi;
  Mov rdi, 8;
  Sub rdi, rdx;

  Mov rax, rsp;
  PrintMemory($channel);
  PopR;
 }

sub PrintOutRaxAsText                                                           # Print rax as text on stdout.
 {PrintRaxAsText($stdout);
 }

sub PrintOutRaxAsTextNL                                                         # Print rax as text on stdout followed by a new line.
 {PrintRaxAsText($stdout);
  PrintOutNL;
 }

sub PrintErrRaxAsText                                                           #P Print rax as text on stderr.
 {PrintRaxAsText($stderr);
 }

sub PrintErrRaxAsTextNL                                                         #P Print rax as text on stderr followed by a new line.
 {PrintRaxAsText($stderr);
  PrintOutNL;
 }

sub PrintRaxAsChar($)                                                           #P Print the ascii character in rax on the specified channel.
 {my ($channel) = @_;                                                           # Channel to write on
  @_ == 1 or confess "One parameter";

  PushR rdi, rax;
  Mov rax, rsp;
  Mov rdi, 1;
  PrintMemory($channel);
  PopR;
 }

sub PrintOutRaxAsChar                                                           # Print the character in rax on stdout.
 {PrintRaxAsChar($stdout);
 }

sub PrintOutRaxAsCharNL                                                         # Print the character in rax on stdout followed by a new line.
 {PrintRaxAsChar($stdout);
  PrintOutNL;
 }

sub PrintErrRaxAsChar                                                           #P Print the character in rax on stderr.
 {PrintRaxAsChar($stderr);
 }

sub PrintErrRaxAsCharNL                                                         #P Print the character in rax on stderr followed by a new line.
 {PrintRaxAsChar($stderr);
  PrintOutNL;
 }

#D1 Variables                                                                   # Variable definitions and operations

#D2 Definitions                                                                 # Variable definitions

sub Variable($;$%)                                                              #P Create a new variable with the specified name initialized via an optional expression.
 {my ($name, $expr, %options) = @_;                                             # Name of variable, optional expression initializing variable, options
  my $size   = 3;                                                               # Size  of variable in bytes as a power of 2
  my $width  = 2**$size;                                                        # Size of variable in bytes
  my $const  = $options{constant}  // 0;                                        # Constant
  my $ref    = $options{reference} // 0;                                        # Reference

  $ref and $const and confess "Reference to constant";

  my $label;                                                                    # Label associated with this variable
  my $position;                                                                 # Position in stack frame or undef for a constant
  if ($const)                                                                   # Constant variable
   {defined($expr) or confess "Value required for constant";
    $expr =~ m(r) and confess
     "Cannot use register expression $expr to initialize a constant";
    $label = Rq($expr);
   }
  else                                                                          # Local variable: Position on stack of variable
   {my $stack = $position = ++$VariableStack[-1];                               # Position in stack frame
    $label = "rbp-8*($stack)";

    if (defined $expr)                                                          # Initialize variable if an initializer was supplied
     {if ($Registers{$expr} and $expr =~ m(\Ar))                                # Expression is ready to go
       {Mov "[$label]", $expr;
       }
      elsif ($expr =~ m(\A\d+\Z))                                               # Transfer constant expression
       {Mov "qword[$label]", $expr;
       }
      else                                                                      # Transfer expression
       {PushR 15;
        Mov r15, $expr;
        Mov "[$label]", r15;
        PopR;
       }
     }
   }

  genHash(__PACKAGE__."::Variable",                                             # Variable definition
    constant  => $const,                                                        # Constant if true
    expr      => $expr,                                                         # Expression that initializes the variable
    label     => $label,                                                        # Address in memory
    name      => $name,                                                         # Name of the variable
    position  => $position,                                                     # Position in stack frame
    reference => $options{reference},                                           # Reference to another variable
   );
 }

sub Nasm::X86::Variable::at($)                                                  # Return a "[register expression]" to address the data in the variable in the current stack frame.
 {my ($variable) = @_;                                                          # Variable descriptor
  "[".$variable->label."]"
 }

sub K($$)                                                                       # Define a constant variable.
 {my ($name, $expr) = @_;                                                       # Name of variable, initializing expression
  &Variable(@_, constant => 1)
 }

sub R($)                                                                        #P Define a reference variable.
 {my ($name) = @_;                                                              # Name of variable
  my $r = &Variable($name);                                                     # The referring variable is 64 bits wide
  $r->reference = 1;                                                            # Mark variable as a reference
  $r                                                                            # Size of the referenced variable
 }

sub V($;$)                                                                      # Define a variable.
 {my ($name, $expr) = @_;                                                       # Name of variable, initializing expression
  &Variable(@_)
 }

#D2 Print variables                                                             # Print the values of variables or the memory addressed by them

sub Nasm::X86::Variable::dump($$$;$$)                                           #P Dump the value of a variable to the specified channel adding an optional title and new line if requested.
 {my ($left, $channel, $newLine, $title1, $title2) = @_;                        # Left variable, channel, new line required, optional leading title, optional trailing title
  @_ >= 3 or confess;
  PushR rax, rdi;
  my $label = $left->label;                                                     # Address in memory
  Mov rax, "[$label]";
  Mov rax, "[rax]" if $left->reference;
  PrintString  ($channel, $title1//$left->name.": ") unless defined($title1) && $title1 eq '';
  PrintRaxInHex($channel);
  PrintString  ($channel, $title2) if defined $title2;

  if ($newLine == 2)                                                            # Print location in the source file in a format that Geany understands
   {my @c = caller 1;
    my (undef, $file, $line) = @c;
    PrintString $channel, "                                                     called at $file line $line";
   }

  PrintNL($channel) if $newLine;
  PopR;
 }

sub Nasm::X86::Variable::err($;$$)                                              #P Dump the value of a variable on stderr.
 {my ($left, $title1, $title2) = @_;                                            # Left variable, optional leading title, optional trailing title
  $left->dump($stderr, 0, $title1, $title2);
 }

sub Nasm::X86::Variable::out($;$$)                                              # Dump the value of a variable on stdout.
 {my ($left, $title1, $title2) = @_;                                            # Left variable, optional leading title, optional trailing title
  $left->dump($stdout, 0, $title1, $title2);
 }

sub Nasm::X86::Variable::errNL($;$$)                                            #P Dump the value of a variable on stderr and append a new line.
 {my ($left, $title1, $title2) = @_;                                            # Left variable, optional leading title, optional trailing title
  $left->dump($stderr, 1, $title1, $title2);
 }

sub Nasm::X86::Variable::d($;$$)                                                #P Dump the value of a variable on stderr and append the source file calling line in a format that Geany understands.
 {my ($left, $title1, $title2) = @_;                                            # Left variable, optional leading title, optional trailing title
  $left->dump($stderr, 2, $title1, $title2);
 }

sub Nasm::X86::Variable::outNL($;$$)                                            # Dump the value of a variable on stdout and append a new line.
 {my ($left, $title1, $title2) = @_;                                            # Left variable, optional leading title, optional trailing title
  $left->dump($stdout, 1, $title1, $title2);
 }

#D3 Decimal representation                                                      # Print out a variable as a decimal number

sub Nasm::X86::Variable::errInDec($;$$)                                         #P Dump the value of a variable on stderr in decimal.
 {my ($number, $title1, $title2) = @_;                                          # Number as variable, optional leading title, optional trailing title
  PrintErrString($title1 // $number->name.": ");
  PushR rax;
  $number->setReg(rax);
  PrintRaxInDec($stderr);
  PopR;
  PrintErrString($title2) if $title2;
 }

sub Nasm::X86::Variable::errInDecNL($;$$)                                       #P Dump the value of a variable on stderr in decimal followed by a new line.
 {my ($number, $title1, $title2) = @_;                                          # Number as variable, optional leading title, optional trailing title
  $number->errInDec($title1, $title2);
  PrintErrNL;
 }

sub Nasm::X86::Variable::outInDec($;$$)                                         # Dump the value of a variable on stdout in decimal.
 {my ($number, $title1, $title2) = @_;                                          # Number as variable, optional leading title, optional trailing title
  PrintOutString($title1 // $number->name.": ");
  PushR rax;
  $number->setReg(rax);
  PrintRaxInDec($stdout);
  PopR;
  PrintOutString($title2) if $title2;
 }

sub Nasm::X86::Variable::outInDecNL($;$$)                                       # Dump the value of a variable on stdout in decimal followed by a new line.
 {my ($number, $title1, $title2) = @_;                                          # Number as variable, optional leading title, optional trailing title
  $number->outInDec($title1, $title2);
  PrintOutNL;
 }

#D3 Decimal representation right justified                                      # Print out a variable as a decimal number right adjusted in a field of specified width

sub Nasm::X86::Variable::rightInDec($$;$)                                       #P Dump the value of a variable on the specified channel as a decimal  number right adjusted in a field of specified width.
 {my ($number, $channel, $width) = @_;                                          # Number as variable, channel, width
  @_ >= 2 or confess "At least two parameters";
  PushR rax;
  $number->setReg(rax);
  PrintRaxRightInDec($width, $channel);
  PopR;
 }

sub Nasm::X86::Variable::errRightInDec($;$)                                     #P Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width.
 {my ($number, $width) = @_;                                                    # Number, width
  @_ >= 1 or confess "At least one parameter";
  $number->rightInDec($stdout, $width);
 }

sub Nasm::X86::Variable::errRightInDecNL($;$)                                   #P Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width followed by a new line.
 {my ($number, $width) = @_;                                                    # Number, width
  @_ >= 1 or confess "At least one parameter";
  $number->rightInDec($stdout, $width);
  PrintErrNL;
 }

sub Nasm::X86::Variable::outRightInDec($;$)                                     # Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width.
 {my ($number, $width) = @_;                                                    # Number, width
  @_ >= 1 or confess "At least one parameter";
  $number->rightInDec($stdout, $width);
 }

sub Nasm::X86::Variable::outRightInDecNL($;$)                                   # Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width followed by a new line.
 {my ($number, $width) = @_;                                                    # Number, width
  @_ >= 1 or confess "At least one parameter";
  $number->rightInDec($stdout, $width);
  PrintOutNL;
 }

#D3 Hexadecimal representation, right justified                                 # Print number variables in hexadecimal right justified in fields of specified width.

sub Nasm::X86::Variable::rightInHex($$;$)                                       #P Write the specified variable number in hexadecimal right justified in a field of specified width to the specified channel.
 {my ($number, $channel, $width) = @_;                                          # Number to print as a variable, channel to print on, width of output field
  @_ >= 2 or confess "At least two parameters";
  PrintRightInHex($channel, $number, $width);
 }

sub Nasm::X86::Variable::errRightInHex($;$)                                     #P Write the specified variable number in hexadecimal right justified in a field of specified width to stderr.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInHex($stderr, $number, $width);
 }

sub Nasm::X86::Variable::errRightInHexNL($;$)                                   #P Write the specified variable number in hexadecimal right justified in a field of specified width to stderr followed by a new line.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInHex($stderr, $number, $width);
  PrintErrNL;
 }

sub Nasm::X86::Variable::outRightInHex($;$)                                     # Write the specified variable number in hexadecimal right justified in a field of specified width to stdout.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInHex($stdout, $number, $width);
 }

sub Nasm::X86::Variable::outRightInHexNL($;$)                                   # Write the specified variable number in hexadecimal right justified in a field of specified width to stdout followed by a new line.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInHex($stdout, $number, $width);
  PrintOutNL;
 }

#D3 Binary representation, right justified                                      # Print number variables in binary right justified in fields of specified width.

sub Nasm::X86::Variable::rightInBin($$;$)                                       #P Write the specified variable number in binary right justified in a field of specified width to the specified channel.
 {my ($number, $channel, $width) = @_;                                          # Number to print as a variable, channel to print on, width of output field
  @_ >= 2 or confess "At least two parameters";
  PrintRightInBin($channel, $number, $width);
 }

sub Nasm::X86::Variable::errRightInBin($;$)                                     #P Write the specified variable number in binary right justified in a field of specified width to stderr.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInBin($stderr, $number, $width);
 }

sub Nasm::X86::Variable::errRightInBinNL($;$)                                   #P Write the specified variable number in binary right justified in a field of specified width to stderr followed by a new line.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInBin($stderr, $number, $width);
  PrintErrNL;
 }

sub Nasm::X86::Variable::outRightInBin($;$)                                     # Write the specified variable number in binary right justified in a field of specified width to stdout.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInBin($stdout, $number, $width);
 }

sub Nasm::X86::Variable::outRightInBinNL($;$)                                   # Write the specified variable number in binary right justified in a field of specified width to stdout followed by a new line.
 {my ($number, $width) = @_;                                                    # Number to print as a variable, width of output field
  @_ >= 1 or confess "At least one parameter";
  PrintRightInBin($stdout, $number, $width);
  PrintOutNL;
 }

#D3 Spaces                                                                      # Print out a variable number of spaces.

sub Nasm::X86::Variable::spaces($$)                                             # Print the specified number of spaces to the specified channel.
 {my ($count, $channel) = @_;                                                   # Number of spaces, channel
  @_ == 2 or confess "Two parameters";
  $count->for(sub {PrintSpace $channel});
 }

sub Nasm::X86::Variable::errSpaces($)                                           #P Print the specified number of spaces to stderr.
 {my ($count) = @_;                                                             # Number of spaces
  @_ == 1 or confess "One parameter";
  $count->spaces($stderr);
 }

sub Nasm::X86::Variable::outSpaces($)                                           # Print the specified number of spaces to stdout.
 {my ($count) = @_;                                                             # Number of spaces
  @_ == 1 or confess "One parameter";
  $count->spaces($stdout);
 }

#D3 C style zero terminated strings                                             # Print out C style zero terminated strings.

sub Nasm::X86::Variable::errCString($)                                          #P Print a zero terminated C style string addressed by a variable on stderr.
 {my ($string) = @_;                                                            # String
  @_ == 1 or confess "One parameter";
  PrintCString($stderr, $string);
 }

sub Nasm::X86::Variable::errCStringNL($)                                        #P Print a zero terminated C style string addressed by a variable on stderr followed by a new line.
 {my ($string) = @_;                                                            # String
  @_ == 1 or confess "One parameter";
  $string->errCString($string);
  PrintErrNL;
 }

sub Nasm::X86::Variable::outCString($)                                          # Print a zero terminated C style string addressed by a variable on stdout.
 {my ($string) = @_;                                                            # String
  @_ == 1 or confess "One parameter";
  PrintCString($stdout, $string);
 }

sub Nasm::X86::Variable::outCStringNL($)                                        # Print a zero terminated C style string addressed by a variable on stdout followed by a new line.
 {my ($string) = @_;                                                            # String
  @_ == 1 or confess "One parameter";
  $string->outCString;
  PrintOutNL;
 }

#D2 Addressing                                                                  # Create references to variables and dereference variables

sub Nasm::X86::Variable::address($)                                             # Create a variable that contains the address of another variable.
 {my ($source) = @_;                                                            # Source variable
  @_ == 1 or confess "One parameter";
  Lea rdi, $source->addressExpr;                                                # Address of variable
  V("addr ".$source->name => rdi)                                               # Return variable containing address of source
 }

sub Nasm::X86::Variable::dereference($)                                         # Create a variable that contains the contents of the variable addressed by the specified variable.
 {my ($address) = @_;                                                           # Source variable
  @_ == 1 or confess "One parameter";
  $address->setReg(rdi);                                                        # Address of referenced variable
  Mov rdi, "[rdi]";                                                             # Content of referenced variable
  V "deref ".$address->name => rdi                                              # Return variable containing content of addressed variable
 }

sub Nasm::X86::Variable::update($$)                                             # Update the content of the addressed variable with the content of the specified variable.
 {my ($address, $content) = @_;                                                 # Source variable, content
  @_ == 2 or confess "Two parameters";
  PushR 14, 15;
  $address->setReg(14);                                                         # Address of referenced variable
  $content->setReg(15);                                                         # Content
  Mov "[r14]", r15;                                                             # Move content to addressed variable;
  PopR;
 }

sub constantString($)                                                           # Return the address and length of a constant string as two variables.
 {my ($string) = @_;                                                            # Constant utf8 string
  use bytes;
  my $L = length($string);
  my $l = K length => $L;
  return ($l, $l) unless $L;
  my $s = V string => Rutf8 $string;
  ($s, $l)
 }

#D2 Operations                                                                  # Variable operations

if (1)                                                                          # Define operator overloading for Variables
 {package Nasm::X86::Variable;
  use overload
    '+'  => \&add,
    '-'  => \&sub,
    '*'  => \&times,
    '/'  => \&divide,
    '%'  => \&mod,
   '=='  => \&eq,
   '!='  => \&ne,
   '>='  => \&ge,
    '>'  => \&gt,
   '<='  => \&le,
   '<'   => \&lt,
   '++'  => \&inc,
   '--'  => \&dec,
   '""'  => \&str,
#  '&'   => \&and,                                                              # We use the zero flag as the bit returned by a Boolean operation so we cannot implement '&' or '|' which were previously in use because '&&' and '||' and "and" and "or" are all disallowed in Perl operator overloading.
#  '|'   => \&or,
   '+='  => \&plusAssign,
   '-='  => \&minusAssign,
   '='   => \&equals,
   '<<'  => \&shiftLeft,
   '>>'  => \&shiftRight,
  '!'    => \&not,
 }

#sub Nasm::X86::Variable::call($)                                                # Execute the call instruction for a target whose address is held in the specified variable.
# {my ($target) = @_;                                                            # Variable containing the address of the code to call
#  $target->setReg(rdi);                                                         # Address of code to call
#  Call rdi;                                                                     # Call referenced code
# }

sub Nasm::X86::Variable::addressExpr($;$)                                       # Create a register expression to address an offset form a variable.
 {my ($left, $offset) = @_;                                                     # Left variable, optional offset
  my $o = $offset ? "+$offset" : "";
  "[".$left-> label."$o]"
 }

sub Nasm::X86::Variable::clone($$)                                              # Clone a variable to make a new variable.
 {my ($variable, $name) = @_;                                                   # Variable to clone, new name for variable
  @_ == 2 or confess "Two parameters";
  my $c = V($name => undef);                                                    # Use supplied name or fall back on existing name
  $c->copy($variable);                                                          # Copy into created variable
  $c                                                                            # Return the clone of the variable
 }

sub Nasm::X86::Variable::copy($$)                                               # Copy one variable into another.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  @_ == 2 or confess "Two parameters";

  if (ref $right)                                                               # Right hand side is a variable expression
   {my $l = $left ->addressExpr;
    my $r = $right->addressExpr;                                                # Variable address

    Mov rdi, $r;                                                                # Load right hand side

    if (ref($right) and $right->reference)                                      # Dereference a reference
     {Mov rdi, "[rdi]";
     }

    if ($left ->reference)                                                      # Copy a reference
     {Mov rsi, $l;
      Mov "[rsi]", rdi;
     }
    else                                                                        # Copy a non reference
     {Mov $l, rdi;
     }
   }
  else                                                                          # Right hand size is a register expression
   {my $l = $left->addressExpr;
    if ($left->reference)                                                       # Copy a constant to a reference
     {my $r = $right =~ m(rsi) ? rdi : rsi;                                     # Choose a transfer register
      Mov $r, $l;                                                               # Load reference
      Mov "qword [$r]", $right;                                                 # Copy to referenced variable
     }
    else                                                                        # Copy a constant to a non reference
     {Mov "qword $l", $right;
     }
   }

  $left                                                                         # Return the variable on the left now that it has had the right hand side copied into it.
 }

sub Nasm::X86::Variable::copyRef($$)                                            # Copy a reference to a variable.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  @_ == 2 or confess "Two parameters";

  $left->reference  or confess "Left hand side must be a reference";

  my $l = $left ->addressExpr;
  my $r = $right->addressExpr;

  if ($right->reference)                                                        # Right is a reference so we copy its value to create a new reference to the original data
   {Mov rdi, $r;
   }
  else                                                                          # Right is not a reference so we copy its address to make a reference to the data
   {Lea rdi, $r;
   }
  Mov $l, rdi;                                                                  # Save value of address in left

  $left;                                                                        # Chain
 }

sub Nasm::X86::Variable::copyZF($)                                              # Copy the current state of the zero flag into a variable.
 {my ($var) = @_;                                                               # Variable
  @_ == 1 or confess "One parameter";

  my $a = $var->addressExpr;                                                    # Address of the variable

  PushR rax;
  Lahf;                                                                         # Save flags to ah: (SF:ZF:0:AF:0:PF:1:CF)
  Shr ah, 6;                                                                    # Put zero flag in bit zero
  And ah, 1;                                                                    # Isolate zero flag
  Mov $a, ah;                                                                   # Save zero flag
  PopR;
 }

sub Nasm::X86::Variable::copyZFInverted($)                                      # Copy the opposite of the current state of the zero flag into a variable.
 {my ($var) = @_;                                                               # Variable
  @_ == 1 or confess "One parameter";

  my $a = $var->addressExpr;                                                    # Address of the variable

  PushR rax, 15;
  Lahf;                                                                         # Save flags to ah: (SF:ZF:0:AF:0:PF:1:CF)
  Shr ah, 6;                                                                    # Put zero flag in bit zero
  Not ah;                                                                       # Invert zero flag
  And ah, 1;                                                                    # Isolate zero flag
  if ($var->reference)                                                          # Dereference and save
   {PushR rdx;
    Mov rdx, $a;
    Mov "[rdx]", ah;                                                            # Save zero flag
    PopR rdx;
   }
  else                                                                          # Save without dereferencing
   {Mov $a, ah;                                                                 # Save zero flag
   }
  PopR;
 }

sub Nasm::X86::Variable::equals($$$)                                            #P Equals operator.
 {my ($op, $left, $right) = @_;                                                 # Operator, left variable,  right variable
  $op
 }

sub Nasm::X86::Variable::assign($$$)                                            #P Assign to the left hand side the value of the right hand side.
 {my ($left, $op, $right) = @_;                                                 # Left variable, operator, right variable
  $left->constant and confess "Cannot assign to a constant";

  Comment "Variable assign";
  Mov rdi, $left ->addressExpr;
  if ($left->reference)                                                         # Dereference left if necessary
   {Mov rdi, "[rdi]";
   }
  if (!ref($right))                                                             # Load right constant
   {Mov rsi, $right;
   }
  else                                                                          # Load right variable
   {Mov rsi, $right->addressExpr;
    if ($right->reference)                                                      # Dereference right if necessary
     {Mov rsi, "[rsi]";
     }
   }
  &$op(rdi, rsi);
  if ($left->reference)                                                         # Store in reference on left if necessary
   {Mov r11, $left->addressExpr;
    Mov "[r11]", rdi;
   }
  else                                                                          # Store in variable
   {Mov $left ->addressExpr, rdi;
   }

  $left;
 }

sub Nasm::X86::Variable::plusAssign($$)                                         #P Implement plus and assign.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  $left->assign(\&Add, $right);
 }

sub Nasm::X86::Variable::minusAssign($$)                                        #P Implement minus and assign.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  $left->assign(\&Sub, $right);
 }

sub Nasm::X86::Variable::arithmetic($$$$)                                       #P Return a variable containing the result of an arithmetic operation on the left hand and right hand side variables.
 {my ($op, $name, $left, $right) = @_;                                          # Operator, operator name, Left variable,  right variable

  my $l = $left ->addressExpr;
  my $r = ref($right) ? $right->addressExpr : $right;                           # Right can be either a variable reference or a constant

  Comment "Arithmetic Start";
  Mov rsi, $l;
  if ($left->reference)                                                         # Dereference left if necessary
   {Mov rsi, "[rsi]";
   }
  Mov rbx, $r;
  if (ref($right) and $right->reference)                                        # Dereference right if necessary
   {Mov rbx, "[rbx]";
   }
  &$op(rsi, rbx);
  my $v = V(join(' ', '('.$left->name, $name, (ref($right) ? $right->name : $right).')'), rsi);
  Comment "Arithmetic End";

  return $v;
 }

sub Nasm::X86::Variable::add($$)                                                #P Add the right hand variable to the left hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::arithmetic(\&Add, q(add), $left, $right);
 }

sub Nasm::X86::Variable::sub($$)                                                #P Subtract the right hand variable from the left hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::arithmetic(\&Sub, q(sub), $left, $right);
 }

sub Nasm::X86::Variable::times($$)                                              #P Multiply the left hand variable by the right hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::arithmetic(\&Imul, q(times), $left, $right);
 }

sub Nasm::X86::Variable::division($$$)                                          #P Return a variable containing the result or the remainder that occurs when the left hand side is divided by the right hand side.
 {my ($op, $left, $right) = @_;                                                 # Operator, Left variable,  right variable

  my $l = $left ->addressExpr;
  my $r = ref($right) ? $right->addressExpr : $right;                           # Right can be either a variable reference or a constant
  PushR rax, rdx, 15;
  Mov rax, $l;
  Mov rax, "[rax]" if $left->reference;
  Mov r15, $r;
  Mov r15, "[r15]" if ref($right) and $right->reference;
  ClearRegisters rdx;                                                           # Failure to do so means that rdx will have junk in it which can cause random divide exceptions that will be incorrectly reported as floating point exceptions.
  Idiv r15;
  my $v = V(join(' ', '('.$left->name, $op, (ref($right) ? $right->name : '').')'), $op eq "%" ? rdx : rax);
  PopR;
  $v;
 }

sub Nasm::X86::Variable::divide($$)                                             #P Divide the left hand variable by the right hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  Nasm::X86::Variable::division("/", $left, $right);
 }

sub Nasm::X86::Variable::mod($$)                                                #P Divide the left hand variable by the right hand variable and return the remainder as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  Nasm::X86::Variable::division("%", $left, $right);
 }

sub Nasm::X86::Variable::shiftLeft($$)                                          #P Shift the left hand variable left by the number of bits specified in the right hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  $left ->setReg(rbx);                                                          # Value to shift
  confess "Variable required not $right" unless ref($right);
  $right->setReg(rcx);                                                          # Amount to shift
  Shl rbx, cl;                                                                  # Shift
  V "shift left" => rbx;                                                        # Save result in a new variable
 }

sub Nasm::X86::Variable::shiftRight($$)                                         #P Shift the left hand variable right by the number of bits specified in the right hand variable and return the result as a new variable.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  $left ->setReg(rbx);                                                          # Value to shift
  confess "Variable required not $right" unless ref($right);
  $right->setReg(rcx);                                                          # Amount to shift
  Shr rbx, cl;                                                                  # Shift
  V "shift right" => rbx;                                                       # Save result in a new variable
 }

sub Nasm::X86::Variable::not($)                                                 #P Form two complement of left hand side and return it as a variable.
 {my ($left) = @_;                                                              # Left variable
  $left->setReg(rdi);                                                           # Value to negate
  Not rdi;                                                                      # Two's complement
  V "neg" => rdi;                                                               # Save result in a new variable
 }

sub Nasm::X86::Variable::booleanZF($$$$)                                        #P Combine the left hand variable with the right hand variable via a boolean operator and indicate the result by setting the zero flag if the result is true.
 {my ($sub, $op, $left, $right) = @_;                                           # Operator, operator name, Left variable,  right variable

  !ref($right) or ref($right) =~ m(Variable) or confess "Variable expected";
  my $r = ref($right) ? $right->addressExpr : $right;                           # Right can be either a variable reference or a constant

  Comment "Boolean ZF Start";

  if ($op =~ m(\Ag)i)
   {($left, $right) = ($right, $left);
   }

  if (ref($right) and $right->reference)                                        # Dereference on right if necessary
   {Mov r11, $left ->addressExpr;
    Mov r11, "[r11]" if $left->reference;
    Mov rdi, $right ->addressExpr;
    Mov rdi, "[rdi]";
    Cmp r11, rdi;
   }
  elsif (ref($right))                                                           # Variable but not a reference on the right
   {Mov r11, $left ->addressExpr;
    Mov r11, "[r11]" if $left->reference;
    Cmp r11, $right->addressExpr;
   }
  elsif ($left->reference)                                                      # Left is a reference, right is a constant
   {Mov r11, $left ->addressExpr;
    Mov r11, "[r11]";
    Cmp r11, $right;
   }
  else                                                                          # Not a reference on the left and a constant on the right
   {Cmp "qword ".$left->addressExpr, $right;
   }
  Comment "Boolean ZF Arithmetic end $op";

  \$op
 }

sub Nasm::X86::Variable::eq($$)                                                 #P Check whether the left hand variable is equal to the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfEq, q(Jne),  $left, $right);
 }

sub Nasm::X86::Variable::ne($$)                                                 #P Check whether the left hand variable is not equal to the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfNe, q(Je), $left, $right);
 }

sub Nasm::X86::Variable::ge($$)                                                 #P Check whether the left hand variable is greater than or equal to the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfGe, q(Jl), $left, $right);
 }

sub Nasm::X86::Variable::gt($$)                                                 #P Check whether the left hand variable is greater than the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfGt, q(Jle), $left, $right);
 }

sub Nasm::X86::Variable::le($$)                                                 #P Check whether the left hand variable is less than or equal to the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfLe, q(Jg), $left, $right);
 }

sub Nasm::X86::Variable::lt($$)                                                 #P Check whether the left hand variable is less than the right hand variable.
 {my ($left, $right) = @_;                                                      # Left variable,  right variable
  Nasm::X86::Variable::booleanZF(\&IfLt, q(Jge), $left, $right);
 }

sub Nasm::X86::Variable::isRef($)                                               # Check whether the specified  variable is a reference to another variable.
 {my ($variable) = @_;                                                          # Variable
  @_ == 1 or confess "One parameter";
  my $n = $variable->name;                                                      # Variable name
  $variable->reference
 }

sub Nasm::X86::Variable::setReg($$)                                             # Set the named registers from the content of the variable.
 {my ($variable, $register) = @_;                                               # Variable, register to load
  @_ == 2 or confess "Two parameters";

  my $r = registerNameFromNumber $register;
  if ($r  =~ m(\Ak?[0-7]\Z))                                                    # Mask register is being set
   {Mov  rdi, $variable->addressExpr;
    if ($variable->isRef)
     {Kmovq $r, "[rdi]";
     }
    else
     {Kmovq $r, rdi;
     }
   }
  else                                                                          # Set normal register
   {if ($variable->isRef)
     {Mov $r, $variable->addressExpr;
      Mov $r, "[$r]";
     }
    else
     {Mov $r, $variable->addressExpr;
     }
   }

  $register                                                                     # Name of register being set
 }

#sub Nasm::X86::Variable::compare($$)                                            # Compare the content of a variable with a numeric constant.
# {my ($variable, $compare) = @_;                                                # Variable, value to compare
#  @_ == 2 or confess "Two parameters";
#
#  if ($variable->isRef)
#   {Mov rsi, $variable->addressExpr;
#    Cmp "qword [rsi]", $compare;
#   }
#  else
#   {Cmp "qword ".$variable->addressExpr, $compare;
#   }
# }

sub Nasm::X86::Variable::getReg($$)                                             # Load the variable from a register expression.
 {my ($variable, $register) = @_;                                               # Variable, register expression to load
  @_ == 2 or confess "Two parameters";
  my $r = registerNameFromNumber $register;
  if ($variable->isRef)                                                         # Move to the location referred to by this variable
   {Comment "Get variable value from register $r";
    my $p = $r eq rdi ? rsi : rdi;
    PushR $p;
    Mov $p, $variable->addressExpr;
    Mov "[$p]", $r;
    PopR $p;
   }
  else                                                                          # Move to this variable
   {Mov $variable->addressExpr, $r;
   }
  $variable                                                                     # Chain
 }

#sub Nasm::X86::Variable::getConst($$)                                           # Load the variable from a constant in effect setting a variable to a specified value.
# {my ($variable, $constant) = @_;                                               # Variable, constant to load
#  @_ == 2 or confess "Two parameters";
#  Mov rdi, $constant;
#  $variable->getReg(rdi);
# }

sub Nasm::X86::Variable::incDec($$)                                             #P Increment or decrement a variable.
 {my ($left, $op) = @_;                                                         # Left variable operator, address of operator to perform inc or dec
  $left->constant and confess "Cannot increment or decrement a constant";
  my $l = $left->addressExpr;
  if ($left->reference)
   {Mov rsi, $l;
    push @text, <<END;
    $op qword [rsi]
END
    return $left;
   }
  else
   {push @text, <<END;
    $op qword $l
END
    return $left;
   }
 }

sub Nasm::X86::Variable::inc($)                                                 #P Increment a variable.
 {my ($left) = @_;                                                              # Variable
  $left->incDec("inc");
 }

sub Nasm::X86::Variable::dec($)                                                 #P Decrement a variable.
 {my ($left) = @_;                                                              # Variable
  $left->incDec("dec");
 }

sub Nasm::X86::Variable::str($)                                                 #P The name of the variable.
 {my ($left) = @_;                                                              # Variable
  $left->name;
 }

sub Nasm::X86::Variable::min($$)                                                # Minimum of two variables.
 {my ($left, $right) = @_;                                                      # Left variable, right variable or constant
  PushR 12, 14, 15;
  $left->setReg(14);

  if (ref($right))                                                              # Right hand side is a variable
   {$right->setReg(15);
   }
  else                                                                          # Right hand side is a constant
   {Mov r15, $right;
   }

  Cmp r14, r15;
  Cmovg  r12, r15;
  Cmovle r12, r14;
  my $r = V("min", r12);
  PopR;
  $r
 }

sub Nasm::X86::Variable::max($$)                                                # Maximum of two variables.
 {my ($left, $right) = @_;                                                      # Left variable, right variable or constant
  PushR 12, 14, 15;
  $left->setReg(14);

  if (ref($right))                                                              # Right hand side is a variable
   {$right->setReg(15);
   }
  else                                                                          # Right hand side is a constant
   {Mov r15, $right;
   }

  Cmp r14, r15;
  Cmovg  r12, r14;
  Cmovle r12, r15;

  my $r = V("max", r12);
  PopR;
  $r
 }

sub Nasm::X86::Variable::and($$)                                                #P And two variables.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  PushR 14, 15;
  Mov r14, 0;
  $left->setReg(15);
  Cmp r15, 0;
  &IfNe (
    sub
     {$right->setReg(15);
      Cmp r15, 0;
      &IfNe(sub {Add r14, 1});
     }
   );
  my $r = V("And(".$left->name.", ".$right->name.")", r14);
  PopR;
  $r
 }

sub Nasm::X86::Variable::or($$)                                                 #P Or two variables.
 {my ($left, $right) = @_;                                                      # Left variable, right variable
  PushR 14, 15;
  Mov r14, 1;
  $left->setReg(15);
  Cmp r15, 0;
  &IfEq (
    sub
     {$right->setReg(15);
      Cmp r15, 0;
      &IfEq(sub {Mov r14, 0});
     }
   );
  my $r = V("Or(".$left->name.", ".$right->name.")", r14);
  PopR;
  $r
 }

sub Nasm::X86::Variable::setMask($$$)                                           # Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.
 {my ($start, $length, $mask) = @_;                                             # Variable containing start of mask, variable containing length of mask, mask register
  @_ == 3 or confess "Three parameters";

  PushR 13, 14, 15;
  Mov r15, -1;
  if ($start)                                                                   # Non zero start
   {$start->setReg(14);
    Bzhi r15, r15, r14;
    Not  r15;
    ref($length) or confess "Not a variable";
    $length->setReg(13);
    Add  r14, r13;
   }
  else                                                                          # Starting at zero
   {confess "Deprecated: use setMaskFirst instead";
     $length->setReg(13);
    Mov r14, $length;
   }
  Bzhi r15, r15, r14;
  Kmovq $mask, r15;
  PopR;
 }

sub Nasm::X86::Variable::setMaskFirst($$)                                       # Set the first bits in the specified mask register.
 {my ($length, $mask) = @_;                                                     # Variable containing length to set, mask register
  @_ == 2 or confess "Two parameters";

  PushR my ($l, $b) = ChooseRegisters(2, $mask);                                # Choose two registers not the mask register
  Mov $b, -1;
  $length->setReg($l);
  Bzhi $b, $b, $l;
  Kmovq $mask, $b if $mask =~ m(\Ak)i;                                          # Set mask register if provided
  Mov   $mask, $b if $mask =~ m(\Ar)i;                                          # Set general purpose register if provided
  PopR;
 }

sub Nasm::X86::Variable::setMaskBit($$)                                         # Set a bit in the specified mask register retaining the other bits.
 {my ($index, $mask) = @_;                                                      # Variable containing bit position to set, mask register
  @_ == 2 or confess "Two parameters";
  $mask =~ m(\Ak)i or confess "Mask register required";
  PushR my ($l, $b) = (r14, r15);
  Kmovq $b, $mask;
  $index->setReg($l);
  Bts $b, $l;
  Kmovq $mask, $b;                                                              # Set mask register if provided
  PopR;
 }

sub Nasm::X86::Variable::clearMaskBit($$)                                       # Clear a bit in the specified mask register retaining the other bits.
 {my ($index, $mask) = @_;                                                      # Variable containing bit position to clear, mask register
  @_ == 2 or confess "Two parameters";
  $mask =~ m(\Ak)i or confess "Mask register required";

  PushR my $l = r14, $b = r15;
  Kmovq $b, $mask;
  $index->setReg($l);
  Btc $b, $l;
  Kmovq $mask, $b;                                                              # Set mask register if provided
  PopR;
 }

sub Nasm::X86::Variable::setBit($$)                                             # Set a bit in the specified register retaining the other bits.
 {my ($index, $mask) = @_;                                                      # Variable containing bit position to set, mask register
  @_ == 2 or confess "Two parameters";

  PushR my ($l) = ChooseRegisters(1, $mask);                                    # Choose a register
  $index->setReg($l);
  Bts $mask, $l;
  PopR;
 }

sub Nasm::X86::Variable::clearBit($$)                                           # Clear a bit in the specified mask register retaining the other bits.
 {my ($index, $mask) = @_;                                                      # Variable containing bit position to clear, mask register
  @_ == 2 or confess "Two parameters";

  PushR my ($l) = ChooseRegisters(1, $mask);                                    # Choose a register
  $index->setReg($l);
  Btc $mask, $l;
  PopR;
 }

sub Nasm::X86::Variable::setZmm($$$$)                                           # Load bytes from the memory addressed by specified source variable into the numbered zmm register at the offset in the specified offset moving the number of bytes in the specified variable.
 {my ($source, $zmm, $offset, $length) = @_;                                    # Variable containing the address of the source, number of zmm to load, variable containing offset in zmm to move to, variable containing length of move
  @_ == 4 or confess;
  ref($offset) && ref($length) or confess "Missing variable";                   # Need variables of offset and length
  Comment "Set Zmm $zmm from Memory";
  PushR 7, 14, 15;
  $offset->setMask($length, k7);                                                # Set mask for target
  $source->setReg(15);
  $offset->setReg(14);                                                          # Position memory for target
  Sub r15, r14;                                                                 # Position memory for target
  Vmovdqu8 "zmm${zmm}{k7}", "[r15]";                                            # Read from memory
  PopR;
 }

#D3 Load mm registers                                                           # Load  zmm registers fom variables and retrieve data from zmm registers into variables.

sub Nasm::X86::Variable::loadZmm($$)                                            # Load bytes from the memory addressed by the specified source variable into the numbered zmm register.
 {my ($source, $zmm) = @_;                                                      # Variable containing the address of the source, number of zmm to get
  @_ == 2 or confess "Two parameters";

  $source->setReg(rdi);
  Vmovdqu8 zmm($zmm), "[rdi]";
 }

sub Nasm::X86::Variable::bFromZ($$$)                                            # Get the byte from the numbered zmm register and put it in a variable.
 {my ($variable, $zmm, $offset) = @_;                                           # Variable, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $variable->copy(getBwdqFromMm 'z', 'b', $zmm, $offset);                       # Get the numbered byte|word|double word|quad word from the numbered zmm register and put it in a variable
 }

sub Nasm::X86::Variable::wFromZ($$$)                                            # Get the word from the numbered zmm register and put it in a variable.
 {my ($variable, $zmm, $offset) = @_;                                           # Variable, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $variable->copy(getBwdqFromMm 'z', 'w', $zmm, $offset);                       # Get the numbered byte|word|double word|quad word from the numbered zmm register and put it in a variable
 }

sub Nasm::X86::Variable::dFromZ($$$)                                            # Get the double word from the numbered zmm register and put it in a variable.
 {my ($variable, $zmm, $offset) = @_;                                           # Variable, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $variable->copy(getBwdqFromMm 'z', 'd', $zmm, $offset);                       # Get the numbered byte|word|double word|quad word from the numbered zmm register and put it in a variable
 }

sub Nasm::X86::Variable::qFromZ($$$)                                            # Get the quad word from the numbered zmm register and put it in a variable.
 {my ($variable, $zmm, $offset) = @_;                                           # Variable, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $variable->copy(getBwdqFromMm 'z', 'q', $zmm, $offset);                       # Get the numbered byte|word|double word|quad word from the numbered zmm register and put it in a variable
 }

sub Nasm::X86::Variable::putBwdqIntoMm($$$$)                                    #P Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register.
 {my ($content, $size, $mm, $offset) = @_;                                      # Variable with content, size of put, numbered zmm, offset in bytes
  @_ == 4 or confess "Four parameters";

  my $o;                                                                        # The offset into the mm register
  if (ref $offset)                                                              # The offset is being passed in a variable
   {$offset->setReg($o = rdi);
   }
  else                                                                          # The offset is being passed as a register expression
   {$o = $offset;
    Comment "Put $size at $offset in $mm";
    $offset >= 0 && $offset <= RegisterSize $mm or
      confess "Out of range" if $offset =~ m(\A\d+\Z);                          # Check the offset if it is a number
   }

  $content->setReg(rsi);
  my $w = RegisterSize $mm;                                                     # Size of mm register
  Vmovdqu32 "[rsp-$w]", $mm;                                                    # Write below the stack
  Mov "[rsp+$o-$w]",  byteRegister(rsi) if $size =~ m(b);                       # Write byte register
  Mov "[rsp+$o-$w]",  wordRegister(rsi) if $size =~ m(w);                       # Write word register
  Mov "[rsp+$o-$w]", dWordRegister(rsi) if $size =~ m(d);                       # Write double word register
  Mov "[rsp+$o-$w]", rsi                if $size =~ m(q);                       # Write register
  Vmovdqu32 $mm, "[rsp-$w]";                                                    # Read below the stack
 }

sub Nasm::X86::Variable::bIntoX($$$)                                            # Place the value of the content variable at the byte in the numbered xmm register.
 {my ($content, $xmm, $offset) = @_;                                            # Variable with content, numbered xmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $content->putBwdqIntoMm('b', "xmm$xmm", $offset)                              # Place the value of the content variable at the word in the numbered xmm register
 }

sub Nasm::X86::Variable::wIntoX($$$)                                            # Place the value of the content variable at the word in the numbered xmm register.
 {my ($content, $xmm, $offset) = @_;                                            # Variable with content, numbered xmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $content->putBwdqIntoMm('w', "xmm$xmm", $offset)                              # Place the value of the content variable at the byte|word|double word|quad word in the numbered xmm register
 }

sub Nasm::X86::Variable::dIntoX($$$)                                            # Place the value of the content variable at the double word in the numbered xmm register.
 {my ($content, $xmm, $offset) = @_;                                            # Variable with content, numbered xmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $content->putBwdqIntoMm('d', "xmm$xmm", $offset)                              # Place the value of the content variable at the byte|word|double word|quad word in the numbered xmm register
 }

sub Nasm::X86::Variable::qIntoX($$$)                                            # Place the value of the content variable at the quad word in the numbered xmm register.
 {my ($content, $xmm, $offset) = @_;                                            # Variable with content, numbered xmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  $content->putBwdqIntoMm('q', "xmm$xmm", $offset)                              # Place the value of the content variable at the byte|word|double word|quad word in the numbered xmm register
 }

sub Nasm::X86::Variable::bIntoZ($$$)                                            # Place the value of the content variable at the byte in the numbered zmm register.
 {my ($content, $zmm, $offset) = @_;                                            # Variable with content, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  checkZmmRegister($zmm);
  $content->putBwdqIntoMm('b', zmm($zmm), $offset)                              # Place the value of the content variable at the word in the numbered zmm register
 }

sub Nasm::X86::Variable::putWIntoZmm($$$)                                       # Place the value of the content variable at the word in the numbered zmm register.
 {my ($content, $zmm, $offset) = @_;                                            # Variable with content, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  checkZmmRegister($zmm);
  $content->putBwdqIntoMm('w', zmm($zmm), $offset)                              # Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register
 }

sub Nasm::X86::Variable::dIntoZ($$$)                                            # Place the value of the content variable at the double word in the numbered zmm register.
 {my ($content, $zmm, $offset) = @_;                                            # Variable with content, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  my $z = extractRegisterNumberFromMM $zmm;
  $content->putBwdqIntoMm('d', "zmm$z", $offset)                                # Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register
 }

sub Nasm::X86::Variable::qIntoZ($$$)                                            # Place the value of the content variable at the quad word in the numbered zmm register.
 {my ($content, $zmm, $offset) = @_;                                            # Variable with content, numbered zmm, offset in bytes
  @_ == 3 or confess "Three parameters";
  checkZmmRegister $zmm;
  $content->putBwdqIntoMm('q', zmm($zmm), $offset)                              # Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register
 }

#D3 At a point                                                                  # Place data into mm registers and retrieve data from them at the indicated point.

sub dFromPointInZ($$%)                                                          #P Get the double word from the numbered zmm register at a point specified by the variable or register and return it in a variable.
 {my ($point, $zmm, %options) = @_;                                             # Point, numbered zmm, options

  my $s = $options{set} // rsi;                                                 # Register to set else a variable will be returned
  my $x = $zmm =~ m(\A(zmm)?0\Z) ? 1 : 0;                                       # The zmm we will extract into
  if (ref($point) =~ m(Variable))  {$point->setReg(k1)}                         # Point is in a variable
  else                             {Kmovq k1, $point}                           # Point is in a register
  my ($z) = zmm $zmm;
  Vpcompressd "zmm$x\{k1}", $z;
  Vpextrd dWordRegister($s), xmm($x), 0;                                        # Extract dword from corresponding xmm
  V d => $s unless $options{set};                                               # Create a variable unless a register to set was provided
 }

sub Nasm::X86::Variable::dFromPointInZ($$%)                                     # Get the double word from the numbered zmm register at a point specified by the variable and return it in a variable.
 {my ($point, $zmm, %options) = @_;                                             # Point, numbered zmm, options
  @_ >= 2 or confess "Two or more parameters";
  dFromPointInZ($point, $zmm, %options);                                        # Register to set else a variable will be returned
 }

sub Nasm::X86::Variable::dIntoPointInZ($$$)                                     # Put the variable double word content into the numbered zmm register at a point specified by the variable.
 {my ($point, $zmm, $content) = @_;                                             # Point, numbered zmm, content to be inserted as a variable
  $content->setReg(rdi);
  $point->setReg(rsi);
  Kmovq k1, rsi;
  Vpbroadcastd zmmM($zmm, 1), edi;                                              # Insert dword at desired location
 }

#D2 Memory                                                                      # Actions on memory described by variables

sub Nasm::X86::Variable::clearMemory($$)                                        # Clear the memory described in this variable.
 {my ($address, $size) = @_;                                                    # Address of memory to clear, size of the memory to clear
  @_ == 2 or confess "Two parameters";
  &ClearMemory($address, $size);                                                # Free the memory
 }

sub Nasm::X86::Variable::copyMemory($$$)                                        # Copy from one block of memory to another.
 {my ($target, $source, $size) = @_;                                            # Address of target, address of source, length to copy
  @_ == 3 or confess "Three parameters";
  &CopyMemory($source, $target, $size);                                         # Copy the memory
 }

sub Nasm::X86::Variable::printMemory($$$)                                       #P Print the specified number of bytes from the memory addressed by the variable on the specified channel.
 {my ($address, $channel, $size) = @_;                                          # Address of memory, channel to print on as a constant, number of bytes to print
  @_ == 3 or confess "Three parameters";

  PushR rax, rdi;
  $address->setReg(rax);
  $size->setReg(rdi);
  &PrintMemory($channel);
  PopR;
 }

sub Nasm::X86::Variable::printErrMemory($$)                                     #P Print the specified number of bytes of the memory addressed by the variable on stdout.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printMemory($stderr, $size);
 }

sub Nasm::X86::Variable::printErrMemoryNL($$)                                   #P Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printErrMemory($size);
  PrintErrNL;
 }

sub Nasm::X86::Variable::printOutMemory($$)                                     # Print the specified number of bytes of the memory addressed by the variable on stdout.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printMemory($stdout, $size);
 }

sub Nasm::X86::Variable::printOutMemoryNL($$)                                   # Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printOutMemory($size);
  PrintOutNL;
 }

sub Nasm::X86::Variable::printMemoryInHexNL($$$)                                #P Write, in hexadecimal, the memory addressed by a variable to stdout or stderr.
 {my ($address, $channel, $size) = @_;                                          # Address of memory, channel to print on, number of bytes to print
  @_ == 3 or confess "Three parameters";
  PushR rax, rdi;
  $address->setReg(rax);
  $size->setReg(rdi);
  &PrintMemoryInHex($channel);
  &PrintNL($channel);
  PopR;
 }

sub Nasm::X86::Variable::printErrMemoryInHexNL($$)                              #P Write the memory addressed by a variable to stderr.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printMemoryInHexNL($stderr, $size);
 }

sub Nasm::X86::Variable::printOutMemoryInHexNL($$)                              # Write the memory addressed by a variable to stdout.
 {my ($address, $size) = @_;                                                    # Address of memory, number of bytes to print
  @_ == 2 or confess "Two parameters";
  $address->printMemoryInHexNL($stdout, $size);
 }

sub Nasm::X86::Variable::freeMemory($$)                                         # Free the memory addressed by this variable for the specified length.
 {my ($address, $size) = @_;                                                    # Address of memory to free, size of the memory to free
  @_ == 2 or confess "Two parameters";
  &FreeMemory($address, $size);                                                 # Free the memory
 }

sub Nasm::X86::Variable::allocateMemory($)                                      # Allocate a variable amount of memory via mmap and return its address.
 {my ($size) = @_;                                                              # Size as a variable
  @_ == 1 or confess "One parameter";
  AllocateMemory($size);
 }

#D2 Structured Programming with variables                                       # Structured programming operations driven off variables.

sub Nasm::X86::Variable::for($&)                                                # Iterate a block a variable number of times.
 {my ($limit, $block) = @_;                                                     # Number of times, Block
  @_ == 2 or confess "Two parameters";
  Comment "Variable::For $limit";
  my $index = V(q(index), 0);                                                   # The index that will be incremented
  my $start = Label;
  my $next  = Label;
  my $end   = Label;
  SetLabel $start;                                                              # Start of loop

  If $index >= $limit, sub {Jge $end};                                          # Condition

  &$block($index, $start, $next, $end);                                         # Execute block

  SetLabel $next;                                                               # Next iteration
  $index++;                                                                     # Increment
  Jmp $start;
  SetLabel $end;
 }

#D1 Operating system                                                            # Interacting with the operating system.

#D2 Processes                                                                   # Create and manage processes

sub Fork()                                                                      # Fork: create and execute a copy of the current process.
 {@_ == 0 or confess;
  Comment "Fork";
  Mov rax, 57;
  Syscall
 }

sub GetPid()                                                                    # Get process identifier.
 {@_ == 0 or confess;
  Comment "Get Pid";

  Mov rax, 39;
  Syscall
 }

sub GetPidInHex()                                                               # Get process identifier in hex as 8 zero terminated bytes in rax.
 {@_ == 0 or confess;
  Comment "Get Pid";
  my $hexTranslateTable = hexTranslateTable;

  my $s = Subroutine
   {SaveFirstFour;
    Mov rax, 39;                                                                # Get pid
    Syscall;
    Mov rdx, rax;                                                               # Content to be printed

    ClearRegisters rax;                                                         # Save a trailing 00 on the stack
    Push ax;
    for my $i(reverse 5..7)
     {my $s = 8*$i;
      Mov rdi,rdx;
      Shl rdi,$s;                                                               # Push selected byte high
      Shr rdi,56;                                                               # Push select byte low
      Shl rdi,1;                                                                # Multiply by two because each entry in the translation table is two bytes long
      Mov ax, "[$hexTranslateTable+rdi]";
      Push ax;
     }
    Pop rax;                                                                    # Get result from stack
    RestoreFirstFourExceptRax;
   } name => "GetPidInHex";

  $s->call;
 }

sub GetPPid()                                                                   # Get parent process identifier.
 {@_ == 0 or confess;
  Comment "Get Parent Pid";

  Mov rax, 110;
  Syscall
 }

sub GetUid()                                                                    # Get userid of current process.
 {@_ == 0 or confess;
  Comment "Get User id";

  Mov rax, 102;
  Syscall
 }

sub WaitPid()                                                                   # Wait for the pid in rax to complete.
 {@_ == 0 or confess;
  Comment "WaitPid - wait for the pid in rax";

    my $s = Subroutine
   {SaveFirstSeven;
    Mov rdi,rax;
    Mov rax, 61;
    Mov rsi, 0;
    Mov rdx, 0;
    Mov r10, 0;
    Syscall;
    RestoreFirstSevenExceptRax;
   } name => "WaitPid";

  $s->call;
 }

sub ReadTimeStampCounter()                                                      # Read the time stamp counter and return the time in nanoseconds in rax.
 {@_ == 0 or confess;

  my $s = Subroutine
   {Comment "Read Time-Stamp Counter";
    PushR rdx;
    ClearRegisters rax;
    Cpuid;
    Rdtsc;
    Shl rdx,32;
    Or rax,rdx;
    PopR;
   } name => "ReadTimeStampCounter";

  $s->call;
 }

#D2 Memory                                                                      # Allocate and print memory

sub PrintMemoryInHex($)                                                         #P Dump memory from the address in rax for the length in rdi on the specified channel. As this method prints in blocks of 8 up to 7 bytes will be missing from the end unless the length is a multiple of 8 .
 {my ($channel) = @_;                                                           # Channel
  @_ == 1 or confess "One parameter";
  Comment "Print out memory in hex on channel: $channel";

  my $s = Subroutine
   {my $size = RegisterSize rax;
    SaveFirstFour;

    Test rdi, 0x7;                                                              # Round the number of bytes to be printed
    IfNz
    Then                                                                        # Round up
     {Add rdi, 8;
     };
    And rdi, 0x3f8;                                                             # Limit the number of bytes to be printed to 1024

    Mov rsi, rax;                                                               # Position in memory
    Lea rdi,"[rax+rdi-$size+1]";                                                # Upper limit of printing with an 8 byte register
    For                                                                         # Print string in blocks
     {Mov rax, "[rsi]";
      Bswap rax;
      PrintRaxInHex($channel);
      Mov rdx, rsi;
      Add rdx, $size;
      Cmp rdx, rdi;
      IfLt
      Then
       {PrintString($channel, "  ");
       }
     } rsi, rdi, $size;
    RestoreFirstFour;
   } name=> "PrintOutMemoryInHexOnChannel$channel";

  $s->call;
 }

sub PrintErrMemoryInHex                                                         #P Dump memory from the address in rax for the length in rdi on stderr.
 {@_ == 0 or confess "No parameters";
  PrintMemoryInHex($stderr);
 }

sub PrintOutMemoryInHex                                                         # Dump memory from the address in rax for the length in rdi on stdout.
 {@_ == 0 or confess "No parameters";
  PrintMemoryInHex($stdout);
 }

sub PrintErrMemoryInHexNL                                                       #P Dump memory from the address in rax for the length in rdi and then print a new line.
 {@_ == 0 or confess "No parameters";
  PrintMemoryInHex($stderr);
  PrintNL($stderr);
 }

sub PrintOutMemoryInHexNL                                                       # Dump memory from the address in rax for the length in rdi and then print a new line.
 {@_ == 0 or confess "No parameters";
  PrintMemoryInHex($stdout);
  PrintNL($stdout);
 }

sub PrintMemory_InHex($)                                                        #P Dump memory from the address in rax for the length in rdi on the specified channel. As this method prints in blocks of 8 up to 7 bytes will be missing from the end unless the length is a multiple of 8 .
 {my ($channel) = @_;                                                           # Channel
  @_ == 1 or confess "One parameter";
  Comment "Print out memory in hex on channel: $channel";

  my $s = Subroutine
   {my $size = RegisterSize rax;
    SaveFirstFour;

    Test rdi, 0x7;                                                              # Round the number of bytes to be printed
    IfNz
    Then                                                                        # Round up
     {Add rdi, 8;
     };
    And rdi, 0x3f8;                                                             # Limit the number of bytes to be printed to 1024

    Mov rsi, rax;                                                               # Position in memory
    Lea rdi,"[rax+rdi-$size+1]";                                                # Upper limit of printing with an 8 byte register
    For                                                                         # Print string in blocks
     {Mov rax, "[rsi]";
      Bswap rax;
      PrintRax_InHex($channel);
      Mov rdx, rsi;
      Add rdx, $size;
      Cmp rdx, rdi;
      IfLt
      Then
       {PrintString($channel, "  ");
       }
     } rsi, rdi, $size;
    RestoreFirstFour;
   } name=> "PrintOutMemory_InHexOnChannel$channel";

  $s->call;
 }

sub PrintErrMemory_InHex                                                        #P Dump memory from the address in rax for the length in rdi on stderr.
 {@_ == 0 or confess;
  PrintMemory_InHex($stderr);
 }

sub PrintOutMemory_InHex                                                        # Dump memory from the address in rax for the length in rdi on stdout.
 {@_ == 0 or confess;
  PrintMemory_InHex($stdout);
 }

sub PrintErrMemory_InHexNL                                                      #P Dump memory from the address in rax for the length in rdi and then print a new line.
 {@_ == 0 or confess;
  PrintMemory_InHex($stderr);
  PrintNL($stderr);
 }

sub PrintOutMemory_InHexNL                                                      # Dump memory from the address in rax for the length in rdi and then print a new line.
 {@_ == 0 or confess;
  PrintMemory_InHex($stdout);
  PrintNL($stdout);
 }

sub PrintMemory($)                                                              #P Print the memory addressed by rax for a length of rdi on the specified channel where channel can be a constant number or a register expression using a bound register.
 {my ($channel) = @_;                                                           # Channel
  @_ == 1 or confess "One parameter";

  SaveFirstFour;
  Mov rsi, rax;
  Mov rdx, rdi;
  Mov rax, 1;                                                                   # Request
  Mov rdi, $channel;                                                            # Channel can be a constant or a register expression
  Syscall;
  RestoreFirstFour;
 }

sub PrintMemoryNL                                                               #P Print the memory addressed by rax for a length of rdi on the specified channel followed by a new line.
 {my ($channel) = @_;                                                           # Channel
  @_ == 1 or confess "One parameter";
  PrintMemory($channel);
  PrintNL($channel);
 }

sub PrintErrMemory                                                              #P Print the memory addressed by rax for a length of rdi on stderr.
 {@_ == 0 or confess;
  PrintMemory($stdout);
 }

sub PrintOutMemory                                                              # Print the memory addressed by rax for a length of rdi on stdout.
 {@_ == 0 or confess;
  PrintMemory($stdout);
 }

sub PrintErrMemoryNL                                                            #P Print the memory addressed by rax for a length of rdi followed by a new line on stderr.
 {@_ == 0 or confess;
  PrintErrMemory;
  PrintErrNL;
 }

sub PrintOutMemoryNL                                                            # Print the memory addressed by rax for a length of rdi followed by a new line on stdout.
 {@_ == 0 or confess;
  PrintOutMemory;
  PrintOutNL;
 }

sub AllocateMemory($)                                                           # Allocate the variable specified amount of memory via mmap and return its address as a variable.
 {my ($size) = @_;                                                              # Size as a variable
  @_ == 1 or confess "Size required";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    Comment "Allocate memory";
    SaveFirstSeven;

    my %d = getSystemConstantsFromIncludeFile "linux/mman.h",                   # Memory map constants
      qw(MAP_PRIVATE MAP_ANONYMOUS PROT_WRITE PROT_READ);

    my $pa = $d{MAP_PRIVATE} | $d{MAP_ANONYMOUS};
    my $wr = $d{PROT_WRITE}  | $d{PROT_READ};

    Mov rax, 9;                                                                 # Memory map
    $$p{size}->setReg(rsi);                                                     # Amount of memory
    Xor rdi, rdi;                                                               # Anywhere
    Mov rdx, $wr;                                                               # Read write protections
    Mov r10, $pa;                                                               # Private and anonymous map
    Mov r8,  -1;                                                                # File descriptor for file backing memory if any
    Mov r9,  0;                                                                 # Offset into file
    Syscall;
    if ($DebugMode)
     {Cmp rax, -1;                                                              # Check return code
      IfEq
      Then
       {PrintErrTraceBack "Cannot allocate memory, return code -1";
       };
      Cmp eax, 0xffffffea;                                                      # Check return code
      IfEq
      Then
       {PrintErrTraceBack "Cannot allocate memory, return code 0xffffffea";
       };
      Cmp rax, -12;                                                             # Check return code
      IfEq
      Then
       {PrintErrTraceBack "Cannot allocate memory, return code -12";
       };
     }
    $$p{address}->getReg(rax);                                                  # Amount of memory
    RestoreFirstSeven;
   } parameters=>[qw(address size)], name => 'AllocateMemory';

  $s->call(parameters=>{size=>$size, address => my $address = V address => 0});

  $address;
 }

sub FreeMemory($$)                                                              # Free memory specified by variables.
 {my ($address, $size) = @_;                                                    # Variable address of memory, variable size of memory
  @_ == 2 or confess "Address, size to free";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    SaveFirstFour;
    Mov rax, 11;                                                                # Munmap
    $$p{address}->setReg(rdi);                                                  # Address
    $$p{size}   ->setReg(rsi);                                                  # Length
    Syscall;
    RestoreFirstFour;
   } parameters=>[qw(size address)], name=> 'FreeMemory';

  $s->call(parameters => {address=>$address, size=>$size});
 }

sub ClearMemory($$)                                                             # Clear memory with a variable address and variable length.
 {my ($address, $size) = @_;                                                    # Address of memory as a variable, size of memory as a variable
  @_ == 2 or confess "address, size required";
  Comment "Clear memory";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    PushR zmm0; PushR rax, rdi, rsi, rdx;                                       # Reliance on push order which no longer matches the order of the arguments
    $$p{address}->setReg(rax);
    $$p{size}   ->setReg(rdi);
    Lea rdx, "[rax+rdi]";                                                       # Address of upper limit of buffer

    ClearRegisters zmm0;                                                        # Clear the register that will be written into memory

    Mov rsi, rdi;                                                               # Modulus the size of zmm
    And rsi, 0x3f;                                                              # Remainder modulo 64
    Cmp rsi, 0;                                                                 # Test remainder
    IfNz sub                                                                    # Need to align so that the rest of the clear can be done in full zmm blocks
     {PushR 7;
      V(align => rsi)->setMaskFirst(k7);                                        # Set mask bits
      Vmovdqu8 "[rax]{k7}", zmm0;                                               # Masked move to memory
      PopR;
      Add rax, rsi;                                                             # Update point to clear from
      Sub rdi, rsi;                                                             # Reduce clear length
     };

    For                                                                         # Clear remaining memory in full zmm blocks
     {Vmovdqu64 "[rax]", zmm0;
     } rax, rdx, RegisterSize zmm0;

    PopR; PopR;
   } parameters=>[qw(size address)], name => 'ClearMemory';

  $s->call(parameters => {address => $address, size => $size});
 }

sub CopyMemory($$$)                                                             # Copy memory.
 {my ($source, $target, $size) = @_;                                            # Source address variable, target address variable, length variable
  @_ == 3 or confess "Source, target, size required";

  SaveFirstSeven;
  $source->setReg(rsi);                                                         # Source location
  $target->setReg(rax);                                                         # Target location
  $size  ->setReg(rdi);                                                         # Size of area to copy
  ClearRegisters rdx;
  For                                                                           # Clear memory
   {Mov "r8b", "[rsi+rdx]";
    Mov "[rax+rdx]", "r8b";
   } rdx, rdi, 1;
  RestoreFirstSeven;
 }

sub CopyMemory64($$$)                                                           # Copy memory in 64 byte blocks.
 {my ($source, $target, $size) = @_;                                            # Source address variable, target address variable, number of 64 byte blocks to move
  @_ == 3 or confess "Source, target, size required";

  PushR my $s = r8, my $t = r9, my $z = r10, my $c = r11, 31;

  $source->setReg($s);                                                          # Source location
  $target->setReg($t);                                                          # Target location
  $size  ->setReg($c);                                                          # Size of area to copy
  my $end = Label;                                                              # End of move loop
  Cmp $c, 0;
  Je $end;                                                                      # Nothing to move
  my $start = SetLabel;                                                         # Move loop
    Vmovdqu64 zmm31, "[$s]";
    Vmovdqu64 "[$t]", zmm31;
    Add $s, 64;
    Add $t, 64;
    Sub $c, 1;
    Jnz $start;
  SetLabel $end;
  PopR;
 }

sub CopyMemory4K($$$)                                                           #P Copy memory in 4K byte blocks.
 {my ($source, $target, $size) = @_;                                            # Source address variable, target address variable, number of 4K byte blocks to move
  @_ == 3 or confess "Source, target, size required";

  PushR my $s = r8, my $t = r9, my $z = r10, my $c = r11, zmm(0..31);
  my $k2 = 2 ** 11;                                                             # Half of 4K == the bytes we can shift in one go using all zmm registers
  $size  ->setReg($c);                                                          # Size of area to copy
  my $end = Label;                                                              # End of move loop
  Cmp $c, 0;
  Je $end;                                                                      # Nothing to move

  $source->setReg($s);                                                          # Source location
  $target->setReg($t);                                                          # Target location
  ClearRegisters $z;                                                            # Offset into move

  my $start = SetLabel;                                                         # Move loop
    Vmovdqu64 "zmm$_", "[$s+$z+64*$_]"              for 0..31;                  # Load 2K
    Vmovdqu64          "[$t+$z+64*$_]",     "zmm$_" for 0..31;                  # Store 2k
    Vmovdqu64 "zmm$_", "[$s+$z+64*$_+$k2]"          for 0..31;                  # Load next 2k
    Vmovdqu64          "[$t+$z+64*$_+$k2]", "zmm$_" for 0..31;                  # Store next 2k
    Add $z, $k2 * 2;                                                            # Next move offset
    Sub $c, 1;                                                                  # Decrement loop counter
    Jnz $start;                                                                 # Continue unless we are finished
  SetLabel $end;
  PopR;
 }

#D2 Files                                                                       # Interact with the operating system via files.

sub OpenRead()                                                                  # Open a file, whose name is addressed by rax, for read and return the file descriptor in rax.
 {@_ == 0 or confess "Zero parameters";

  my $s = Subroutine
   {my %s = getSystemConstantsFromIncludeFile  "fcntl.h", qw(O_RDONLY);         # Constants for reading a file

    SaveFirstFour;
    Mov rdi,rax;
    Mov rax,2;
    Mov rsi, $s{O_RDONLY};
    Xor rdx,rdx;
    Syscall;
    RestoreFirstFourExceptRax;
   } name=> "OpenRead";

  $s->call;
 }

sub OpenWrite()                                                                 # Create the file named by the terminated string addressed by rax for write.  The file handle will be returned in rax.
 {@_ == 0 or confess "Zero parameters";

  my $s = Subroutine
   {my %s = getSystemConstantsFromIncludeFile "fcntl.h", qw(O_CREAT O_WRONLY);  # Constants for creating a file
    my $w = $s{O_WRONLY} | $s{O_CREAT};

    SaveFirstFour;
    Mov rdi, rax;
    Mov rax, 2;
    Mov rsi, $w;
    Mov rdx, 0x1c0;                                                             # Permissions: u=rwx  1o=x 4o=r 8g=x 10g=w 20g=r 40u=x 80u=r 100u=r 200=T 400g=S 800u=S #0,2,1000, nothing
    Syscall;

    RestoreFirstFourExceptRax;
   } name=> "OpenWrite";

  $s->call;
 }

sub CloseFile()                                                                 # Close the file whose descriptor is in rax.
 {@_ == 0 or confess "Zero parameters";

  my $s = Subroutine
   {Comment "Close a file";
    SaveFirstFour;
    Mov rdi, rax;
    Mov rax, 3;
    Syscall;
    RestoreFirstFourExceptRax;
   } name=> "CloseFile";

  $s->call;
 }

sub StatSize()                                                                  # Stat a file whose name is addressed by rax to get its size in rax.
 {@_ == 0 or confess "Zero parameters";

  my ($F, $S) = (q(sys/stat.h), q(struct stat));                                # Get location of struct stat.st_size field
  my $Size = getStructureSizeFromIncludeFile $F, $S;
  my $off  = getFieldOffsetInStructureFromIncludeFile $F, $S, q(st_size);

  my $s = Subroutine
   {Comment "Stat a file for size";
    SaveFirstFour;
    Mov rdi, rax;                                                               # File name
    Mov rax,4;
    Lea rsi, "[rsp-$Size]";
    Syscall;
    Mov rax, "[$off+rsp-$Size]";                                                # Place size in rax
    RestoreFirstFourExceptRax;
   } name=> "StatSize";

  $s->call;
 }

sub ReadChar()                                                                  # Read a character from stdin and return it in rax else return -1 in rax if no character was read.
 {@_ == 0 or confess "Zero parameters";
  my $s = Subroutine
   {my ($p) = @_;
    SaveFirstFour;                                                              # Generated code

    Mov rax, 0;                                                                 # Read
    Mov rdi, 0;                                                                 # Stdin
    Lea rsi, "[rsp-8]";                                                         # Make space on stack
    Mov rdx, 1;                                                                 # One character
    Syscall;

    Cmp rax, 1;
    IfEq
    Then
     {Mov al, "[rsp-8]";
     },
    Else
     {Mov rax, -1;
     };

    RestoreFirstFourExceptRax;
   } name => 'ReadChar';

  $s->call
 }

sub ReadLine()                                                                  # Reads up to 8 characters followed by a terminating return and place them into rax.
 {@_ == 0 or confess "Zero parameters";
  my $s = Subroutine
   {my ($p) = @_;
    PushR rcx, 14, 15;
    ClearRegisters rax, rcx, r14, r15;

    (V max => RegisterSize(rax))->for(sub                                       # Read each character
     {my ($index, $start, $next, $end) = @_;

      ReadChar;
      Cmp rax, 0xf0;                                                            # Too high
      IfGe Then {Jmp $end};
      Cmp rax, 0xa;                                                             # Too low
      IfLe Then {Jmp $end};
      $index->setReg(rcx);
      Shl rcx, 3;
      Shl rax, cl;                                                              # Move into position
      Or r15, rax;
      Add rcx, $bitsInByte;
     });

    Mov rax, r15;                                                               # Return result in rax
    PopR;
   } name => 'ReadLine';

  $s->call
 }

sub ReadInteger()                                                               # Reads an integer in decimal and returns it in rax.
 {@_ == 0 or confess "Zero parameters";
  my $s = Subroutine
   {my ($p) = @_;
    PushR 15;
    ClearRegisters rax, r15;

    (V max => RegisterSize(rax))->for(sub                                       # Read each character
     {my ($index, $start, $next, $end) = @_;

      ReadChar;
      Cmp rax, 0x3A;                                                            # Too high
      IfGe Then {Jmp $end};
      Cmp rax, 0x29;                                                            # Too low
      IfLe Then {Jmp $end};
      Imul r15, 10;                                                             # Move into position
      Sub rax, 0x30;
      Add r15, rax;
     });

    Mov rax, r15;                                                               # Return result in rax
    PopR;
   } name => 'ReadInteger';

  $s->call
 }

sub ReadFile($)                                                                 # Read a file into memory.
 {my ($File) = @_;                                                              # Variable addressing a zero terminated string naming the file to be read in by mapping it
  @_ == 1 or confess "One parameter required";

  my $s = Subroutine
   {my ($p) = @_;
    Comment "Read a file into memory";
    SaveFirstSeven;                                                             # Generated code
    my $size = V(size => undef);
    my $fdes = V(fdes => undef);

    $$p{file}->setReg(rax);                                                     # File name

    StatSize;                                                                   # File size
    $size->getReg(rax);                                                         # Save file size

    $$p{file}->setReg(rax);                                                     # File name
    OpenRead;                                                                   # Open file for read
    $fdes->getReg(rax);                                                         # Save file descriptor

    my %d  = getSystemConstantsFromIncludeFile                                  # Memory map constants
     "linux/mman.h", qw(MAP_PRIVATE PROT_READ PROT_EXEC);
    my $pa = $d{MAP_PRIVATE};
    my $ro = $d{PROT_READ};
    my $ex = $d{PROT_EXEC};

    Mov rax, 9;                                                                 # Memory map
    $size->setReg(rsi);                                                         # Amount of memory
    Xor rdi, rdi;                                                               # Anywhere
    Mov rdx, $ro | $ex;                                                         # Read/execute contents
    Mov r10, $pa;                                                               # Private and anonymous map
    $fdes->setReg(r8);                                                          # File descriptor for file backing memory
    Mov r9,  0;                                                                 # Offset into file
    Syscall;
    $size       ->setReg(rdi);
    $$p{address}->getReg(rax);
    $$p{size}   ->getReg(rdi);
    RestoreFirstSeven;
   } parameters=>[qw(file address size)], name => 'ReadFile';

  my $file    = ref($File) ? $File : V file => Rs $File;
  my $size    = V(size    => undef);
  my $address = V(address => undef);
  $s->call(parameters=>{file => $file, size=>$size, address=>$address});

  ($address, $size)                                                             # Return address and size of mapped file
 }

sub executeFileViaBash($)                                                       # Execute the file named in a variable.
 {my ($file) = @_;                                                              # File variable
  @_ == 1 or confess "File required";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    SaveFirstFour;
    Fork;                                                                       # Fork

    Test rax, rax;

    IfNz                                                                        # Parent
    Then
     {WaitPid;
     },
    Else                                                                        # Child
     {$$p{file}->setReg(rdi);
      Mov rsi, 0;
      Mov rdx, 0;
      Mov rax, 59;
      Syscall;
     };
    RestoreFirstFour;
   } parameters=>[qw(file)], name => 'executeFileViaBash';

  $s->call(parameters=>{file => $file});
 }

sub unlinkFile($)                                                               # Unlink the named file.
 {my ($file) = @_;                                                              # File variable
  @_ == 1 or confess "File required";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    SaveFirstFour;
    $$p{file}->setReg(rdi);
    Mov rax, 87;
    Syscall;
    RestoreFirstFour;
   } parameters=>[qw(file)], name => 'unlinkFile';

  $s->call(parameters=>{file => $file});
 }

#D1 Hash functions                                                              # Hash functions

sub Hash()                                                                      # Hash a string addressed by rax with length held in rdi and return the hash code in r15.
 {@_ == 0 or confess;

  my $s = Subroutine                                                            # Read file
   {Comment "Hash";

    PushR rax, rdi, k1, zmm0, zmm1;                                             # Save registers
    PushR 15;                                                                   # Return register
    Vpbroadcastq zmm0, rdi;                                                     # Broadcast length through ymm0
    Vcvtuqq2pd   zmm0, zmm0;                                                    # Convert to lengths to float
    Vgetmantps   zmm0, zmm0, 4;                                                 # Normalize to 1 to 2, see: https://hjlebbink.github.io/x86doc/html/VGETMANTPD.html

    Add rdi, rax;                                                               # Upper limit of string

    ForIn                                                                       # Hash in ymm0 sized blocks
     {Vmovdqu ymm1, "[rax]";                                                    # Load data to hash
      Vcvtudq2pd zmm1, ymm1;                                                    # Convert to float
      Vgetmantps zmm0, zmm0, 4;                                                 # Normalize to 1 to 2, see: https://hjlebbink.github.io/x86doc/html/VGETMANTPD.html

      Vmulpd zmm0, zmm1, zmm0;                                                  # Multiply current hash by data
     }
    sub                                                                         # Remainder in partial block
     {Mov r15, -1;
      Bzhi r15, r15, rdi;                                                       # Clear bits that we do not wish to load
      Kmovq k1, r15;                                                            # Take up mask
      Vmovdqu8 "ymm1{k1}", "[rax]";                                             # Load data to hash

      Vcvtudq2pd zmm1, ymm1;                                                    # Convert to float
      Vgetmantps   zmm0, zmm0, 4;                                               # Normalize to 1 to 2, see: https://hjlebbink.github.io/x86doc/html/VGETMANTPD.html

      Vmulpd zmm0, zmm1, zmm0;                                                  # Multiply current hash by data
     }, rax, rdi, RegisterSize ymm0;

    Vgetmantps   zmm0, zmm0, 4;                                                 # Normalize to 1 to 2, see: https://hjlebbink.github.io/x86doc/html/VGETMANTPD.html

    Mov r15, 0b11110000;                                                        # Top 4 to bottom 4
    Kmovq k1, r15;
    Vpcompressq  "zmm1{k1}", zmm0;
    Vaddpd       ymm0, ymm0, ymm1;                                              # Top 4 plus bottom 4

    Mov r15, 0b1100;                                                            # Top 2 to bottom 2
    Kmovq k1, r15;
    Vpcompressq  "ymm1{k1}", ymm0;
    Vaddpd       xmm0, xmm0, xmm1;                                              # Top 2 plus bottom 2

    Pslldq       xmm0, 2;                                                       # Move centers into double words
    Psrldq       xmm0, 4;
    Mov r15, 0b0101;                                                            # Centers to lower quad
    Kmovq k1, r15;
    Vpcompressd  "xmm0{k1}", xmm0;                                              # Compress to lower quad
    PopR r15;

    Vmovq r15, xmm0;                                                            # Result in r15

    PopR;
   } name=> "Hash";

  $s->call;
 }

#D1 Unicode                                                                     # Convert between utf8 and utf32

sub convert_rax_from_utf32_to_utf8                                              #P Convert a utf32 character held in rax to a utf8 character held in rax.
 {@_ and confess "Zero parameters";

  my $s = Subroutine
   {PushR 14, 15;
    Block
     {my ($success) = @_;                                                       # As shown at: https://en.wikipedia.org/wiki/UTF-8
      Cmp rax, 0x7f;                                                            # Ascii
      IfLe Then {Jmp $success};

      Cmp rax, 0x7ff;                                                           # Char size is: 2 bytes
      IfLe
      Then
       {Mov r15, rax;

        Shr r15, 6;                                                             # High byte
        And r15, 0x1f;
        Or  r15, 0xc0;

        Mov r14, rax;                                                           # Low byte
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 8;
        Or r15, r14;
        Mov rax, r15;
        Jmp $success;
       };

      Cmp rax, 0xffff;                                                          # Char size is: 3 bytes
      IfLe
      Then
       {Mov r15, rax;

        Shr r15, 12;                                                            # High byte
        And r15, 0x0f;
        Or  r15, 0xe0;

        Mov r14, rax;                                                           # Middle byte
        Shr r14, 6;
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 8;
        Or r15, r14;

        Mov r14, rax;                                                           # Low byte
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 16;
        Or r15, r14;

        Mov rax, r15;
        Jmp $success;
       };

      Cmp rax, 0x10ffff;                                                        # Char size is: 4 bytes
      IfLe
      Then
       {Mov r15, rax;

        Shr r15, 18;                                                            # High byte
        And r15, 0x03;
        Or  r15, 0xf0;

        Mov r14, rax;                                                           # Middle byte
        Shr r14, 12;
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 8;
        Or r15, r14;

        Mov r14, rax;                                                           # Middle byte
        Shr r14, 6;
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 16;
        Or r15, r14;

        Mov r14, rax;                                                           # Low byte
        And r14, 0x3f;
        Or  r14, 0x80;
        Shl r14, 24;
        Or r15, r14;
        Mov rax, r15;
        Jmp $success;
       };
     };

    PopR;
   } name => 'convert_rax_from_utf32_to_utf8';

  $s->call;
 } # convert_rax_from_utf32_to_utf8

sub GetNextUtf8CharAsUtf32($)                                                   # Get the next UTF-8 encoded character from the addressed memory and return it as a UTF-32 character as a variable along with the size of the input character and a variable indicating the success - 1 -  or failure  - 0 - of the operation.
 {my ($in) = @_;                                                                # Address of utf8 character as a variable
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters

    $$p{fail}->copy(0);                                                         # Clear failure indicator
    $$p{in}->setReg(rbx);                                                       # Character to convert
    ClearRegisters rdx;                                                         # Move to byte register below does not clear the entire register
    Mov dl, "[rbx]";
    Block
     {my ($success) = @_;                                                       # As shown at: https://en.wikipedia.org/wiki/UTF-8

      Cmp rdx, 0x7f;                                                            # Ascii
      IfLe
      Then
       {$$p{out}->getReg(rdx);
        $$p{size}->copy(1);
        Jmp $success;
       };

      Cmp rdx, 0xdf;                                                            # Char size is: 2 bytes
      IfLe
      Then
       {Mov dil, "[rbx+1]";
        And rdi, 0x3f;
        And rdx, 0x1f;
        Shl rdx, 6;
        Or  rdx,  rdi;
        $$p{out}->getReg(rdx);
        $$p{size}->copy(2);
        Jmp $success;
       };

      Cmp rdx, 0xef;                                                            # Char size is: 3 bytes
      IfLe
      Then
       {Mov sil, "[rbx+2]";
        And rsi, 0x3f;
        Mov dil, "[rbx+1]";
        And rdi, 0x3f;
        And rdx, 0x0f;
        Shl rdi,  6;
        Shl rdx, 12;
        Or  rdx,  rdi;
        Or  rdx,  rsi;
        $$p{out}->getReg(rdx);
        $$p{size}->copy(3);
        Jmp $success;
       };

      Cmp rdx, 0xf7;                                                            # Char size is: 4 bytes
      IfLe
      Then
       {Mov r11b, "[rbx+3]";
        And r11, 0x3f;
        Mov sil, "[rbx+2]";
        And rsi, 0x3f;
        Mov dil, "[rbx+1]";
        And rdi, 0x3f;
        And rdx, 0x07;
        Shl rsi,  6;
        Shl rdi, 12;
        Shl rdx, 18;
        Or  rdx,  rdi;
        Or  rdx,  rsi;
        Or  rdx,  r11;
        $$p{out}->getReg(rdx);
        $$p{size}->copy(4);
        Jmp $success;
       };

      $$p{fail}->copy(1);                                                       # Conversion failed
     };

   } parameters=>[qw(in out  size  fail)], name => 'GetNextUtf8CharAsUtf32';

  my $out  = V(out  => 0);                                                      # Utf32 equivalent
  my $size = V(size => 0);                                                      # Size of utf8 converted
  my $fail = V(fail => 0);                                                      # Failed if true else false

  $s->inline(parameters=>{in=>$in, out=>$out, size=>$size, fail=>$fail});

 ($out, $size, $fail)                                                           # Output character variable, output size of input, output error if any
 } # GetNextUtf8CharAsUtf32

sub ConvertUtf8ToUtf32($$)                                                      # Convert an allocated string of utf8 to an allocated string of utf32 and return its address and length.
 {my ($a8, $s8) = @_;                                                           # Utf8 string address variable, utf8 length variable
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    PushR 10, 11, 12, 13, 14, 15;

    my $a8      = $$p{a8};                                                      # Address of utf8
    my $s8      = $$p{s8};                                                      # Length of utf8 in bytes
    my $size    = $$p{s8} * RegisterSize(eax);                                  # Maximum possible length of corresponding utf32
    my $address = AllocateMemory $size;                                         # Allocate a buffer for utf32
    $$p{a32}->copy($address);                                                   # Address of allocation
    $$p{s32}->copy($size);                                                      # Size of allocation

     $a8     ->setReg(14);                                                      # Current position in input string
    ($a8+$s8)->setReg(15);                                                      # Upper limit of input string
    $address->setReg(13);                                                       # Current position in output string
    ClearRegisters 12;                                                          # Number of characters in output string

    $s8->for(sub                                                                # Loop through input string  converting each utf8 sequence to utf32
     {my ($index, $start, $next, $end) = @_;
      my ($out, $size, $fail) = GetNextUtf8CharAsUtf32 V(in => r14);            # Get next utf-8 character and convert it to utf32
      If $fail > 0,
      Then
       {$$p{fail}->copy($fail);
        Jmp $end;
       };

      Inc r12;                                                                  # Count characters converted
      $out->setReg(r11);                                                        # Output character

      Mov  "[r13]",  r11d;
      Add    r13,    RegisterSize eax;                                          # Move up 32 bits output string
      $size->setReg(r10);                                                       # Decoded this many bytes
      Add   r14, r10;                                                           # Move up in input string
      Cmp   r14, r15;
      Jge $end;                                                                 # Exhausted input string
    });

    $$p{count}->getReg(r12);                                                    # Number of unicode points converted from utf8 to utf32
    PopR;
   } parameters=>[qw(a8 s8 a32 s32 count fail)], name => 'ConvertUtf8ToUtf32';

  my $a32   = V(a32   => 0);
  my $s32   = V(s32   => 0);
  my $count = V(count => 0);
  my $fail  = V(fail  => 0);                                                    # Assume we will succeed

  $s->call(parameters=>
    {a8  => $a8,  s8  => $s8,
     a32 => $a32, s32 => $s32, count=>$count, fail => $fail});

  ($a32, $s32, $count, $fail)                                                   # Utf32 string address as a variable, utf32 area length as a variable, number of characters converted, fail if one else zero
 } # ConvertUtf8ToUtf32

#D1 C Strings                                                                   # C strings are a series of bytes terminated by a zero byte.

sub Cstrlen()                                                                   #P Length of the C style string addressed by rax returning the length in r15.
 {@_ == 0 or confess "Deprecated in favor of StringLength";

  my $s = Subroutine                                                            # Create area
   {PushR rax, rdi, rcx;
    Mov rdi, rax;
    Mov rcx, -1;
    ClearRegisters rax;
    push @text, <<END;
    repne scasb
END
    Mov r15, rcx;
    Not r15;
    Dec r15;
    PopR;
   } name => "Cstrlen";

  $s->call;
 }

sub StringLength($)                                                             # Length of a zero terminated string.
 {my ($string) = @_;                                                            # String
  @_ == 1 or confess "One parameter: zero terminated string";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    PushR rax, rdi, rcx;
    $$p{string}->setReg(rax);                                                   # Address string
    Mov rdi, rax;
    Mov rcx, -1;
    ClearRegisters rax;
    push @text, <<END;
    repne scasb
END
    Not rcx;
    Dec rcx;
    $$p{size}->getReg(rcx);                                                     # Save length
    PopR;
   } parameters => [qw(string size)], name => 'StringLength';

  $s->call(parameters=>{string=>$string, size => my $z = V size => 0});         # Variable that holds the length of the string

  $z
 }

#D1 Areas                                                                       # An area is single extensible block of memory which contains other data structures such as strings, arrays, trees within it.

#D2 Constructors                                                                # Construct an area either in memory or by reading it from a file or by incorporating it into an assembly.

sub DescribeArea(%)                                                             #P Describe a relocatable area.  By describing an areas, we allocate space with which to describe it in the current stack frame but we do not allocate memory for the area itself in the heap.
 {my (%options) = @_;                                                           # Optional variable addressing the start of the area
  my $address   = delete $options{address}   // 0;                              # Address of area
  my $stack     = delete $options{stack}     // 0;                              # Mark this area as being used as a stack
  confess "Invalid options: ".join(", ", sort keys %options) if keys %options;  # Complain about any invalid options

  my $B = 12;                                                                   # Log2 of size of initial allocation for the area
  my $N = 2 ** $B;                                                              # Initial size of area
  my $w = RegisterSize 31;

  my $quad = RegisterSize rax;                                                  # Field offsets of quad words used in the header to
  my $size = 0;
  my $used = $size + $quad;                                                     # Amount of memory in the area that has been used - includes the free chain.
  my $free = $used + $quad;                                                     # Free chain blocks = freed zmm blocks
  my $tree = $free + $quad;                                                     # Start of Yggdrasil,
  my $data = $w;                                                                # Data starts in the next zmm block

  genHash(__PACKAGE__."::Area",                                                 # Definition of area
    B          => $B,                                                           # Log2 of size of initial allocation
    N          => $N,                                                           # Initial allocation
    stack      => $stack,                                                       # MArk the area as being used as a stack
    sizeOffset => $size,                                                        # Size field offset
    usedOffset => $used,                                                        # Used field offset
    freeOffset => $free,                                                        # Free chain offset
    treeOffset => $tree,                                                        # Yggdrasil - a tree of global variables in this area
    dataOffset => $data,                                                        # The start of the data
    address    => ($address || V address => 0),                                 # Variable that addresses the memory containing the area
    zmmBlock   => $w,                                                           # Size of a zmm block - 64 bytes
    nextOffset => $w - RegisterSize(eax),                                       # Position of next offset on free chain
   );
 }

sub CreateArea(%)                                                               # Create an relocatable area and returns its address in rax. We add a chain header so that 64 byte blocks of memory can be freed and reused within the area.
 {my (%options) = @_;                                                           # Free=>1 adds a free chain.
  my $area = DescribeArea(%options);                                            # Describe an area
  my $N     = $area->N;
  my $used  = $area->usedOffset;
  my $data  = $area->dataOffset;
  my $size  = $area->sizeOffset;

  my $s = Subroutine                                                            # Allocate area
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $area = AllocateMemory K size=> $N;                                      # Allocate memory and save its location in a variable

    PushR rax;
    $$s{area}->address->copy($area);                                            # Save address of area
    $area->setReg(rax);
    Mov "dword[rax+$used]", $data;                                              # Initially used space
    Mov "dword[rax+$size]", $N;                                                 # Size
    PopR;
   } structures=>{area=>$area}, name => 'CreateArea';

  $s->call(structures=>{area=>$area});                                          # Variable that holds the reference to the area which is updated when the area is reallocated

  $area
 }

sub ReadArea($)                                                                 # Read an area stored in a file into memory and return an area descriptor for the area so created.
 {my ($file) = @_;                                                              # Name of file to read
  my ($address, $size) = ReadFile $file;                                        # Read the file into memory
  DescribeArea address => $address;                                             # Describe it as an area
 }

sub loadAreaIntoAssembly($)                                                     # Load an area into the current assembly and return a descriptor for it.
 {my ($file) = @_;                                                              # File containing an area

  if (my $l = $loadAreaIntoAssembly{$file})                                     # Check for a pre existing area
   {return DescribeArea address => V area=>$l;                                  # Describe area
   }

  my  $areaFinish = $loadAreaIntoAssembly{$file} = Label;                       # Label at start of area
  Jmp $areaFinish;                                                              # Jump over area
  push @text, <<END;
  align 16
END
  my  $areaStart = SetLabel;
  Incbin qq("$file");                                                           # Include area as a binary file
  SetLabel $areaFinish;

  DescribeArea address => V area=>$areaStart;                                   # Describe area
 }

sub Nasm::X86::Area::free($)                                                    # Free an area.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  FreeMemory($area->address, $area->size)
 }

sub Nasm::X86::Area::copyDescriptor($$)                                         #P Copy the description of one area into another
 {my ($target, $source) = @_;                                                   # Target area, source area

  $target->address->copy($source->address);                                     # target now addresses same area as source
  $target
 }

#D2 Memory                                                                      # Manage memory controlled by an area.

sub Nasm::X86::Area::used($)                                                    # Return the currently used size of an area as a variable.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  SaveFirstFour;
  $area->address->setReg(rax);                                                  # Address area
  Mov rdx, "[rax+$$area{usedOffset}]";                                          # Used
  Sub rdx, $area->dataOffset;                                                   # Subtract size of header so we get the actual amount in use
  my $size = V 'area used up' => rdx;                                           # Save length in a variable
  RestoreFirstFour;
  $size                                                                         # Return variable length
 }

sub Nasm::X86::Area::size($)                                                    # Get the size of an area as a variable.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  PushR rax;
  $area->address->setReg(rax);                                                  # Address area
  Mov rax, "[rax+$$area{sizeOffset}]";                                          # Get size
  my $size = V 'size of area' => rax;                                           # Save size in a variable
  PopR;
  $size                                                                         # Return size
 }

sub Nasm::X86::Area::updateSpace($$)                                            #P Make sure that a variable addressed area has enough space to accommodate content of a variable size.
 {my ($area, $size) = @_;                                                       # Area descriptor, variable size needed
  @_ == 2 or confess "Two parameters";
  my $base     = rdi;                                                           # Base of area
  my $newSize  = rsi;                                                           # New size needed
  my $areaSize = "[$base+$$area{sizeOffset}]";                                  # Size of area
  my $areaUsed = "[$base+$$area{usedOffset}]";                                  # Used space in area

  my $s = Subroutine
   {my ($p, $s)  = @_;                                                          # Parameters, structures
    PushR my $base = r15, my $newSize = r14, my $proposed = r13;                # Base of area, New size needed, Proposed size
    my $areaSize = "[$base+$$area{sizeOffset}]";                                # Size of area
    my $areaUsed = "[$base+$$area{usedOffset}]";                                # Used space in area

    my $area = $$s{area};                                                       # Area
    $area->address->setReg($base);                                              # Address area

    $$p{size}->setReg($newSize);                                                # Space requested
    Add $newSize, $areaUsed;                                                    # Space needed in area

    Mov $proposed, $areaSize;                                                   # Minimum proposed area size

    K(loop=>36)->for(sub                                                        # Maximum number of shifts
     {my ($index, $start, $next, $end) = @_;
      Shl $proposed, 1;                                                         # New proposed size
      Cmp $proposed, $newSize;                                                  # Big enough?
      Jge $end;                                                                 # Big enough!
     });

    my $address = AllocateMemory V size => $proposed;                           # Create new area
    CopyMemory4K($area->address, $address, $area->size>>K(k4 => $area->B));     # Copy old area into new area 4K at a time
    FreeMemory $area->address, $area->size;                                     # Free previous memory previously occupied area
    $area->address->copy($address);                                             # Save new area address
    $address->setReg($base);                                                    # Address area
    Mov "[$base+$$area{sizeOffset}]", $proposed;                                # Save the new size in the area

    PopR;
   } parameters => [qw(size)],
     structures => {area => $area},
     name       => 'Nasm::X86::Area::updateSpace';

  $area->address->setReg($base);                                                # Address area
  $size->setReg($newSize);                                                      # Space requested
  Add $newSize, $areaUsed;                                                      # Space needed in area
  Cmp $newSize, $areaSize;                                                      # Compare size needed with current size
  IfGt                                                                          # New size is bigger than current size
  Then                                                                          # More space needed
   {$s->call(parameters=>{size => $size}, structures=>{area => $area});         # Allocate more space for area
   };
 } # updateSpace

sub Nasm::X86::Area::makeReadOnly($)                                            # Make an area read only.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    Comment "Make an area readable";
    SaveFirstFour;
    $$p{address}->setReg(rax);
    Mov rdi, rax;                                                               # Address of area
    Mov rsi, "[rax+$$area{sizeOffset}]";                                        # Size of area

    Mov rdx, 1;                                                                 # Read only access
    Mov rax, 10;
    Syscall;
    RestoreFirstFour;                                                           # Return the possibly expanded area
   } parameters=>[qw(address)], name => 'Nasm::X86::Area::makeReadOnly';

  $s->call(parameters=>{address => $area->address});
 }

sub Nasm::X86::Area::makeWriteable($)                                           # Make an area writable.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    Comment "Make an area writable";
    SaveFirstFour;
    $$p{address}->setReg(rax);
    Mov rdi, rax;                                                               # Address of area
    Mov rsi, "[rax+$$area{sizeOffset}]";                                        # Size of area
    Mov rdx, 3;                                                                 # Read only access
    Mov rax, 10;
    Syscall;
    RestoreFirstFour;                                                           # Return the possibly expanded area
   } parameters=>[qw(address)], name => 'Nasm::X86::Area::makeWriteable';

  $s->call(parameters=>{address => $area->address});
 }

#D2 Alloc/Free                                                                  # Allocate and free memory in an area, either once only but in variable size blocks or reusably in zmm sized blocks via the free block chain.

sub Nasm::X86::Area::allocate($$)                                               # Allocate the variable amount of space in the variable addressed area and return the offset of the allocation in the area as a variable.
 {my ($area, $Size) = @_;                                                       # Area descriptor, variable amount of allocation
  @_ == 2 or confess "Two parameters";

  my $size =  ref($Size) ? $Size : K size => $Size;                             # Promote constant

  $area->updateSpace($size);                                                    # Update space if needed
  $area->address->setReg(rax);
  Mov rsi, "[rax+$$area{usedOffset}]";                                          # Currently used
  my $offset = V(offset => rsi);                                                # Variable to hold offset of allocation
  $size  ->setReg(rdi);
  Add rsi, rdi;
  Mov "[rax+$$area{usedOffset}]", rsi;                                          # Update currently used

  $offset
 }

sub Nasm::X86::Area::allocZmmBlock($)                                           # Allocate a block to hold a zmm register in the specified area and return the offset of the block as a variable.
 {my ($area) = @_;                                                              # Area
  @_ == 1 or confess "One parameter";
  my $offset = V(offset => 0);                                                  # Variable to hold offset of allocation

  PushR rax;

  $area->address->setReg(rax);                                                  # Address of area
  my $firstBlock = "dword[rax+$$area{freeOffset}]";                             # Offset of first block in free chain if such a block exists

  Cmp $firstBlock, 0;
  IfGt                                                                          # Check free chain
  Then                                                                          # Free block available on free chain
   {PushR my $first = r14, my $second = r15, my $block = 31;
    my $firstD  = $first.'d';
    my $secondD = $second.'d';
    Mov $firstD, $firstBlock;                                                   # Offset of first block
    $area->getZmmBlock(V(offset => $first), $block);                            # Load the first block on the free chain
    dFromZ($block, 0)->setReg($second);                                         # The location of the second block if any
    Mov $firstBlock, $secondD;                                                  # Offset of first block in free chain if such a block exists
    ClearRegisters $block;                                                      # Clear the zmm block - possibly this only needs to be done if we are reusing a block
    $offset->getReg($first);                                                    # First block is the allocated block
    $area->putZmmBlock($offset, $block);
    PopR;
   },
  Else                                                                          # Cannot reuse a free block so allocate
   {$offset->copy($area->allocate(K size => $area->zmmBlock));                  # Copy offset of allocation
   };

  PopR;

  $offset                                                                       # Return offset of allocated block
 }

sub Nasm::X86::Area::allocZmmBlock3($)                                          #P Allocate three zmm blocks in one go and return their offsets.
 {my ($area) = @_;                                                              # Area
  @_ == 1 or confess "One parameter";
  my $o1 = V(o1 => 0);                                                          # First block
  my $o2 = V(o2 => 0);                                                          # Second block
  my $o3 = V(o3 => 0);                                                          # Third block

  PushR rax;

  $area->address->setReg(rax);                                                  # Address of area
  my $firstBlock = "dword[rax+$$area{freeOffset}]";                             # Offset of first block in free chain if such a block exists

  Cmp $firstBlock, 0;
  IfGt                                                                          # Check free chain
  Then                                                                          # Free block available on free chain
   {$o1->copy($area->allocZmmBlock);
    $o2->copy($area->allocZmmBlock);
    $o3->copy($area->allocZmmBlock);
   },
  Else                                                                          # Cannot reuse a free block so allocate
   {$o1->copy($area->allocate(K size => $area->zmmBlock * 3));                  # Copy offset of allocation
    $o2->copy($o1 + RegisterSize zmm0);                                         # Cut out sub blocks
    $o3->copy($o2 + RegisterSize zmm0);
   };

  PopR;

  ($o1, $o2, $o3)                                                               # Return offsets of allocated blocks
 }

sub Nasm::X86::Area::freeZmmBlock($$)                                           # Free a block in an area by placing it on the free chain.
 {my ($area, $offset) = @_;                                                     # Area descriptor, offset of zmm block to be freed
  @_ == 2 or confess "Two parameters";

  PushR rax, my $first = r14, my $second = r15, zmm7;
  my $firstD = $first.'d'; my $secondD = $second.'d';
  $area->address->setReg(rax);                                                  # Address of area
  Mov $secondD, "[rax+$$area{freeOffset}]";                                     # Offset of first block in free chain if such a block exists
  ClearRegisters zmm7;
  Movd xmm7, $secondD;
  $area->putZmmBlock($offset, 7);
  $offset->setReg($first);                                                      # Offset if what will soon be the first block on the free chain
  Mov "[rax+$$area{freeOffset}]", $firstD;                                      # Offset of first block in free chain if such a block exists
  PopR;
 }

sub Nasm::X86::Area::freeChainSpace($)                                          # Count the number of blocks available on the free chain.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameters";
  my $count = V('free chain blocks' => 0);

  PushR rax, my $first = r15, 31;
  my $firstD = $first.'d';

  $area->address->setReg(rax);                                                  # Address of area
  Mov $firstD, "[rax+$$area{freeOffset}]";                                      # Offset of first block in free chain if such a block exists
  K( loop => 99)->for(sub                                                       # Loop through free block chain
   {my ($index, $start, $next, $end) = @_;
    Cmp $first, 0;
    IfEq Then{Jmp $end};                                                        # No more free blocks
    $area->getZmmBlock(V(offset => $first), 31);                                # Load the first block on the free chain
    dFromZ(31, 0)->setReg($first);                                              # The location of the second block if any
    $count++                                                                    # Increment count of number of  blocks on the free chain
   });
  PopR;
  $count * RegisterSize 31;
 }

sub Nasm::X86::Area::getZmmBlock($$$)                                           # Get the block with the specified offset in the specified string and return it in the numbered zmm.
 {my ($area, $block, $zmm) = @_;                                                # Area descriptor, offset of the block as a variable or register, number of zmm register to contain block
  @_ == 3 or confess "Three parameters";

  my $a = rdi;                                                                  # Work registers
  my $o = ref($block) =~ m(Variable) ? rsi : $block;                            # Offset of block in area via register or variable

  $area->address->setReg($a);                                                   # Area address
  $block        ->setReg($o) if ref($block) =~ m(Variable);                     # Offset of block in area via variable

  if ($DebugMode)
   {Cmp $o, $area->dataOffset;
    IfLt                                                                        # We could have done this using variable arithmetic, but such arithmetic is expensive and so it is better to use register arithmetic if we can.
    Then
     {PrintErrTraceBack "Attempt to get block before start of area";
     };
   }

  Vmovdqu64 zmm($zmm), "[$a+$o]";                                               # Read from memory
 }

sub Nasm::X86::Area::putZmmBlock($$$)                                           # Write the numbered zmm to the block at the specified offset in the specified area.
 {my ($area, $block, $zmm) = @_;                                                # Area descriptor, offset of the block as a variable, number of zmm register to contain block
  @_ == 3 or confess "Three parameters";

  my $a = rdi;                                                                  # Work registers
  my $o = ref($block) =~ m(Variable) ? rsi : $block;

  $area->address->setReg($a);                                                   # Area address
  $block->setReg($o) if ref($block) =~ m(Variable);                             # Offset of block in area

  if ($DebugMode)
   {Cmp $o, $area->dataOffset;
    IfLt                                                                        # We could have done this using variable arithmetic, but such arithmetic is expensive and so it is better to use register arithmetic if we can.
    Then
     {PrintErrTraceBack "Attempt to put block before start of area";
     };
   }
  Vmovdqu64 "[$a+$o]", zmm($zmm);                                               # Read from memory
 }

sub Nasm::X86::Area::clearZmmBlock($$)                                          # Clear the zmm block at the specified offset in the area.
 {my ($area, $offset) = @_;                                                     # Area descriptor, offset of the block as a variable
  @_ == 2 or confess "Two parameters";

  ClearRegisters 1;
  $area->putZmmBlock($offset, 1);
 }

#D2 Yggdrasil                                                                   # The world tree from which we can address so many other things

sub Nasm::X86::Yggdrasil::UniqueStrings        {K key => 0}                     #P A tree of strings that assigns unique numbers to strings.
sub Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1}                     #P Translates a string number into the offset of a subroutine in an area.
sub Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2}                     #P Maps the unique string number for a subroutine name to the offset in the are that contains the length (as a dword) followed by the string content of the Perl data structure describing the subroutine in question.
sub Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3}                     #P Unisyn alphabets.
sub Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4}                     #P Open bracket to close bracket
sub Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5}                     #P Close bracket to open bracket
sub Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6}                     #P Permissible transitions from alphabet to alphabet

sub Nasm::X86::Area::yggdrasil($)                                               # Return a tree descriptor to the Yggdrasil world tree for an area creating the world tree Yggdrasil if it has not already been created.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $t = $area->DescribeTree;                                                  # Tree descriptor for Yggdrasil
  PushR rax, r15;
  $area->address->setReg(rax);                                                  # Address underlying area
  Mov r15, "[rax+$$area{treeOffset}]";                                          # Address Yggdrasil

  Cmp r15, 0;                                                                   # Does Yggdrasil even exist?
  IfNe
  Then                                                                          # Yggdrasil has already been created so we can address it
   {$t->first->getReg(r15);
   },
  Else                                                                          # Yggdrasil has not been created
   {my $T = $area->CreateTree;
    $t->copyDescriptor($T);
    $T->first->setReg(r15);
    $area->address->setReg(rax);                                                # Address underlying area - it might have moved
    Mov "[rax+$$area{treeOffset}]", r15;                                        # Save offset of Yggdrasil
   };
  PopR;
  $t
 }

sub Nasm::X86::Area::checkYggdrasilCreated($)                                   #P Return a tree descriptor for the Yggdrasil world tree for an area. If Yggdrasil has not been created the found field of the returned descriptor will have zero in it else one.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $t = $area->DescribeTree;                                                  # Tree descriptor for Yggdrasil
  PushR rax;
  $area->address->setReg(rax);                                                  #P Address underlying area
  Mov rax, "[rax+$$area{treeOffset}]";                                          # Address Yggdrasil
  my $v = V('Yggdrasil', rax);                                                  # Offset to Yggdrasil if it exists else zero
  Cmp rax, 0;                                                                   # Does Yggdrasil even exist?
  IfNe
  Then                                                                          # Yggdrasil has been created so we can address it
   {$t->first->copy(rax);
    $t->found->copy(1);
   },
  Else                                                                          # Yggdrasil has not been created
   {$t->found->copy(0);
   };
  PopR rax;
  $t
 }

#D2 Areas as Strings                                                            # Use the memory supplied by the area as a string - however, in general, this is too slow unless coupled with another slow operation such as executing a command, mapping a file or writing to a file.

sub Nasm::X86::Area::appendMemory($$$)                                          # Append the variable addressed content in memory of variable size to the specified area  and return its offset in that area. Pre-pack data as much as possible before using this routine to optimize processing.
 {my ($area, $address, $size) = @_;                                             # Area descriptor, variable address of content, variable length of content
  @_ == 3 or confess "Three parameters";

  my $used = "[rax+$$area{usedOffset}]";                                        # Address the used field in the area

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $area = $$s{area};
    $area->address->setReg(rax);                                                # Address area
    my $oldUsed = V("used", $used);                                             # Record currently used space
    $area->updateSpace($$p{size});                                              # Update space if needed

    my $target  = $oldUsed + $area->address;                                    # Where to write the copied memory to
    CopyMemory($$p{address}, $target, $$p{size});                               # Copy data into the area

    my $newUsed = $oldUsed + $$p{size};                                         # Amount of space now being used

    $area->address->setReg(rax);                                                # Update used field
    $newUsed->setReg(rdi);
    Mov $used, rdi;

    $$p{offset}->copy($oldUsed);                                                # Return offset of content in area

   } structures => {area => $area},
     parameters => [qw(address size offset)],
     name       => 'Nasm::X86::Area::m';

  my $offset = V offset => 0;                                                   # Offset within the area at which the content was appended
  $s->inline(structures => {area    => $area},
             parameters => {address => $address, size => $size,
                            offset  => $offset});
  $offset
 }

sub Nasm::X86::Area::appendZmm($$)                                              # Append the contents of the specified zmm to the specified area and returns its offset in that area as a variable,
 {my ($area, $zmm) = @_;                                                        # Area descriptor, zmm number
  @_ == 2 or confess "Two parameters";

  my $k = $area->allocZmmBlock;                                                 # Allocate a block to hold the content of the zmm register

  $area->address->setReg(rax);
  $k->setReg(rbx);
  Vmovdqu64 "[rax+rbx]", zmm($zmm);                                             # Copy in the data held in the supplied zmm register

  $k                                                                            # Return offset in area
 }

sub Nasm::X86::Area::appendVar($$)                                              # Append the contents of a variable to the specified area
 {my ($area, $var) = @_;                                                        # Area descriptor, variable
  @_ == 2 or confess "Two parameters";

  Lea rax, $var->addressExpr;
  $area->appendMemory(V(address => rax), V size => qSize)                       # Return offset in area
 }

sub Nasm::X86::Area::q($$)                                                      # Append a constant string to the area.
 {my ($area, $string) = @_;                                                     # Area descriptor, string
  @_ == 2 or confess "Two parameters";

  my $s = Rs($string);
  $area->appendMemory(V('address', $s), V('size', length($string)));
 }

sub Nasm::X86::Area::ql($$)                                                     # Append a constant quoted string containing new line characters to the specified area.
 {my ($area, $const) = @_;                                                      # Area, constant
  @_ == 2 or confess "Two parameters";
  for my $l(split /\s*\n/, $const)
   {$area->q($l);
    $area->nl;
   }
 }

sub Nasm::X86::Area::char($$)                                                   # Append a constant character expressed as a decimal number to the specified area.
 {my ($area, $char) = @_;                                                       # Area descriptor, number of character to be appended
  @_ == 2 or confess "Two parameters";
  my $s = Rb(ord($char));
  $area->appendMemory(V(address => $s), K size => 1);                           # Move data
 }

sub Nasm::X86::Area::nl($)                                                      # Append a new line to the area addressed by rax.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  $area->char("\n");
 }

sub Nasm::X86::Area::zero($)                                                    #P Append a trailing zero to the area addressed by rax.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  $area->char("\0");
 }

sub Nasm::X86::Area::append($@)                                                 # Append one area to another.
 {my ($target, $source) = @_;                                                   # Target area descriptor, source area descriptor
  @_ == 2 or confess "Two parameters";

  SaveFirstFour;
  $source->address->setReg(rax);
  Mov rdi, "[rax+$$source{usedOffset}]";
  Sub rdi, $source->dataOffset;
  Lea rsi, "[rax+$$source{dataOffset}]";
  $target->appendMemory(V(address => rsi), V size => rdi);
  RestoreFirstFour;
 }

sub Nasm::X86::Area::clear($)                                                   # Clear an area but keep it at the same size.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    PushR rax, rdi;
    $$p{address}->setReg(rax);
    Mov rdi, $area->dataOffset;
    Mov "[rax+$$area{usedOffset}]", rdi;
    ClearRegisters rdi;
    Mov "[rax+$$area{freeOffset}]", rdi;
    Mov "[rax+$$area{treeOffset}]", rdi;
    PopR;
   } parameters=>[qw(address)], name => 'Nasm::X86::Area::clear';

  $s->call(parameters=>{address => $area->address});
 }

sub Nasm::X86::Area::read($$)                                                   # Read a file specified by a variable addressed zero terminated string and append its content to the specified area.
 {my ($area, $file) = @_;                                                       # Area descriptor, variable addressing file name
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Comment "Read an area";
    my ($address, $size) = ReadFile $$p{file};
    my $area = $$s{area};
    $area->appendMemory($address, $size);                                       # Move data into area
    FreeMemory($size, $address);                                                # Free memory allocated by read
   } structures => {area=>$area},
     parameters => [qw(file)],
     name       => 'Nasm::X86::Area::read';

  $s->call(structures => {area => $area}, parameters => {file => $file});
 }

sub Nasm::X86::Area::write($$)                                                  # Write the content of the specified area to a file specified by a zero terminated string.
 {my ($area, $file) = @_;                                                       # Area descriptor, variable addressing zero terminated file name
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    SaveFirstFour;
    $$p{file}->setReg(rax);
    OpenWrite;                                                                  # Open file
    my $file = V(fd => rax);                                                    # File descriptor

    $$p{address}->setReg(rsi);                                                  # Write from start of area
    Mov rdx, "[rsi+$$area{usedOffset}]";                                        # Length of the area to write

    Mov rax, 1;                                                                 # Write content to file
    $file->setReg(rdi);                                                         # File number
    Syscall;

    $file->setReg(rax);                                                         # Close the file
    CloseFile;
    RestoreFirstFour;
   } parameters=>[qw(file address)], name => 'Nasm::X86::Area::write';

  my $f = ref($file) ? $file : V file => Rs $file;                              # Convert constant string to zero terminated string

  $s->call(parameters=>{address => $area->address, file => $f});
 }

sub Nasm::X86::Area::out($)                                                     # Print the specified area on sysout.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p) = @_;                                                               # Parameters
    SaveFirstFour;
    $$p{address}->setReg(rax);

    Mov rdi, "[rax+$$area{usedOffset}]";                                        # Length to print
    Sub rdi, $area->dataOffset;                                                 # Length to print
    Lea rax, "[rax+$$area{dataOffset}]";                                        # Address of data field
    PrintOutMemory;
    RestoreFirstFour;
   } parameters=>[qw(address)], name => 'Nasm::X86::Area::out';

  $s->call(parameters=>{address => $area->address});
 }

sub Nasm::X86::Area::outNL($)                                                   # Print the specified area on sysout followed by a new line.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";

  $area->out;
  PrintOutNL;
 }

sub Nasm::X86::Area::printOut($$$)                                              # Print part of the specified area on sysout.
 {my ($area, $Offset, $Length) = @_;                                            # Area descriptor, offset, length
  @_ == 3 or confess "Three parameters";

  my $offset = ref($Offset) ? $Offset : K offset => $Offset;
  my $length = ref($Length) ? $Length : K length => $Length;

  PushR rax, rdi, rsi;
  ($area->address + $offset)->setReg(rax);
  $length                   ->setReg(rdi);
  PrintOutMemoryNL;
  PopR;
 }

sub Nasm::X86::Area::dump($$;$)                                                 # Dump details of an area.
 {my ($area, $title, $depth) = @_;                                              # Area descriptor, title string, optional variable number of 64 byte blocks to dump
  @_ == 2 or @_ == 3 or confess "Two or three parameters";
  my $blockSize = 64;                                                           # Print in blocks of this size

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    PushR rax, rdi;
    my $area = $$s{area};
    $area->address->setReg(rax);                                                # Get address of area
    PrintOutString("Area   ");

    PushR rax;                                                                  # Print size
    Mov rax, "[rax+$$area{sizeOffset}]";
    PrintOutString "  Size: ";
    PrintOutRightInDec rax, 8;
    PrintOutString "  ";
    PopR rax;

    PushR rax;                                                                  # Print size
    Mov rax, "[rax+$$area{usedOffset}]";
    PrintOutString("  Used: ");
    PrintOutRightInDec rax, 8;
    PrintOutNL;
    PopR rax;

    $$p{depth}->for(sub                                                         # Print the requested number of blocks
     {my ($index, $start, $next, $end) = @_;
      Mov rdi, $blockSize;                                                      # Length of each print
      ($index*RegisterSize(zmm31))->out('', ' | ');
      my $address = $area->address + $index * $blockSize;                       # Address of block to print
      $address->setReg(rax);
      PrintOutMemory_InHexNL;
     });

    PopR;
   } structures=>{area=>$area},
     parameters=>[qw(depth)],
     name => "Nasm::X86::Area::dump";

  PrintOutStringNL $title;
  $s->call(structures=>{area=>$area}, parameters=>{depth => defined($depth) ? (ref($depth) ? $depth : V(depth => $depth)) : V('depth', 4)});
 }

#D2 Areas as Stacks                                                             # Use an area as a stack. If the area is simultaneouls used for other operations confusion will ensue.

sub Nasm::X86::Area::push($$)                                                   # Push the contents of a variable into an area
 {my ($area, $var) = @_;                                                        # Area descriptor, variable
  @_ == 2 or confess "Two parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack

  $area->updateSpace(K size => my $w = RegisterSize rax);                       # Update space if needed

  my $oldUsed = rsi, my $target = rdi, my $areaA = rax, my $dataToPush = rdx;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space

  Mov $dataToPush, $var->addressExpr;                                           # Data to push
  Mov "[$oldUsed + $areaA]", $dataToPush;                                       # Push data into the area
  Lea $target, "[$oldUsed + $w]";                                               # Amount of space now being used

  $area->address->setReg($areaA);                                               # Update used field
  Mov $used, $target;
 }

sub Nasm::X86::Area::stackSize($)                                               # Size of the stack in an area being used as a stack
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack

  my $oldUsed = rsi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space
  Sub $oldUsed, $area->dataOffset;                                              # Space minus header is actual space used by stack
  V stackSize => $oldUsed;                                                      # Return stack size as a variable
 }

sub Nasm::X86::Area::peek($$)                                                   # Peek at a variable on the stack
 {my ($area, $back) = @_;                                                       # Area descriptor, how far back to look in the stack with the top most element being at one.
  @_ == 2 or confess "Two parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack
  confess "Constant number required" unless $back =~ m(\A\d+\Z);                # Number of variables to go back

  my $w = RegisterSize rax;                                                     # Size of a variable
  my $oldUsed = rsi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space
  Cmp $oldUsed, $area->dataOffset + $back * $w - 1;                             # Space minus header is actual space used by stack
  IfLe                                                                          # Stack under flow
  Then
   {PrintErrTraceBack "Stack underflow";
   };

  V pop => "[$oldUsed + $areaA - $back * $w]";                                  # Return popped data
 }

sub Nasm::X86::Area::stackVariable($$)                                          # Peek at the variable indexed variable on the stack
 {my ($area, $index) = @_;                                                      # Area descriptor, index of element sought
  @_ == 2 or confess "Two parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack
  confess "Variable required" unless ref $index;                                # Index of element must be a variable

  my $w = RegisterSize rax;                                                     # Size of a variable
  my $Index = rsi, my $Area = rax;

  $area ->address->setReg($Area);                                               # Address area
  $index->setReg($Index);                                                       # Index of element

  V pop => "[$Area+$w*$Index+$$area{dataOffset}]";                              # Return popped data
 }

sub Nasm::X86::Area::stackVariableSize($)                                       # Size of a stack of variables in an area.
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack

  $area ->address->setReg(rax);                                                 # Address area

  Mov rax, "[rax+$$area{usedOffset}]";                                          # Used area in bytes
  Sub rax, $area->dataOffset;                                                   # Header space
  Shr rax, 3;                                                                   # Used area in quads
  V size => rax;                                                                # Return size in quads
 }

sub Nasm::X86::Area::pop($)                                                     # Pop a variable from the stack in an area being used as a stack
 {my ($area) = @_;                                                              # Area descriptor
  @_ == 1 or confess "One parameter";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack

  my $w = RegisterSize rax;                                                     # Size of a variable
  my $oldUsed = rsi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space
  Cmp $oldUsed, $area->dataOffset + $w;                                         # Space minus header is actual space used by stack
  IfLt                                                                          # Stack under flow
  Then
   {PrintErrTraceBack "Stack underflow";
   };

  Sub $used, $w;                                                                # Reduce stack by one variable
  V pop => "[$oldUsed + $areaA - $w]";                                          # Return popped data
 }

sub Nasm::X86::Area::pushZmm($$)                                                # Push the contents of a zmm register into an area.
 {my ($area, $zmm) = @_;                                                        # Area descriptor, zmm number
  @_ == 2 or confess "Two parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack

  $area->updateSpace(K size => my $w = RegisterSize zmm1);                      # Update space if needed

  my $oldUsed = rsi, my $target = rdi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space

  Vmovdqu64 "[$oldUsed + $areaA]", zmm($zmm);                                   # Push data into the area
  Lea $target, "[$oldUsed + $w]";                                               # Amount of space now being used

  $area->address->setReg($areaA);                                               # Update used field
  Mov $used, $target;

  V zmmStackOffset => $oldUsed;                                                 # Offset of the zmm in the stack
 }

sub Nasm::X86::Area::peekZmm($$$)                                               # Peek at a zmm register from the stack in an area being used as a stack and return a variable containing its offset in the area so we can update the pushed zmm if desired.
 {my ($area, $zmm, $back) = @_;                                                 # Area descriptor, zmm number, how far back to look in the stack with the top most element being at one.
  @_ == 3 or confess "Three parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack
  confess "Constant number required" unless $back =~ m(\A\d+\Z);                # Number of variables to go back
  my $w = RegisterSize zmm1;                                                    # Size of a zmm register

  my $oldUsed = rsi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space
  Sub $oldUsed, $back * $w;                                                     # Back up
  Cmp $oldUsed, $area->dataOffset - 1;                                          # Space minus header is actual space used by stack
  IfLe                                                                          # Stack under flow
  Then
   {PrintErrTraceBack "Stack underflow when peeking for zmm";
   };

  Vmovdqu64 zmm($zmm), "[$oldUsed + $areaA]";                                   # Set zmm to popped data
  V offset => $oldUsed;                                                         # Return offset of zmm block
 }

sub Nasm::X86::Area::popZmm($$)                                                 # Pop a zmm register from the stack in an area being used as a stack
 {my ($area, $zmm) = @_;                                                        # Area descriptor, zmm number
  @_ == 2 or confess "Two parameters";
  confess "Not a stack" unless $area->stack;                                    # Check that we are using the area as a stack
  my $w = RegisterSize zmm1;                                                    # Size of a zmm register

  my $oldUsed = rsi, my $areaA = rax;
  my $used = "qword[$areaA+$$area{usedOffset}]";                                # Address the used field in the area

  $area->address->setReg($areaA);                                               # Address area
  Mov $oldUsed, $used;                                                          # Record currently used space
  Cmp $oldUsed, $area->dataOffset + $w - 1;                                     # Space minus header is actual space used by stack
  IfLe                                                                          # Stack under flow
  Then
   {PrintErrTraceBack "Stack underflow when popping zmm";
   };

  Sub $used, $w;                                                                # Reduce stack by one variable
  Vmovdqu64 zmm($zmm), "[$oldUsed + $areaA - $w]";                              # Set zmm to popped data
 }

#D1 Tree                                                                        # Tree constructed as sets of blocks in an area.

#D2 Constructors                                                                # Construct a tree.

sub DescribeTree(%)                                                             #P Return a descriptor for a tree with the specified options.  The options from one tree get inherited by any sub trees they contain
 {my (%options)  = @_;                                                          # Tree description options
  my $area       = delete $options{area};                                       # The area containing the tree
  my $length     = delete $options{length};                                     # Maximum number of keys per node
  my $name       = delete $options{name};                                       # Optional name for debugging purposes
  my $stringTree = delete $options{stringTree};                                 # Tree of strings - the key offsets designate 64 byte blocks of memory in the same area that contains the tree.  If a key string is longer than 64 bytes then the rest of it appears in the sub tree indicated by the data element.
  confess "Invalid options: ".join(", ", sort keys %options) if keys %options;  # Complain about any invalid options
     $length     = 13;                                                          # Maximum number of keys per node

  my $b = RegisterSize 31;                                                      # Size of a block == size of a zmm register
  my $o = RegisterSize eax;                                                     # Size of a double word

  my $keyAreaWidth = $b - $o * 2 ;                                              # Key / data area width  in bytes
  my $kwdw   = $keyAreaWidth / $o;                                              # Number of keys in a maximal block

  confess "Length: $length is even not odd" unless $length % 2;                 # Ideally length is odd
  confess "Length must be greater than 2, not: $length" unless $length > 2;     # Check minimum length
  confess "Length must be less than or equal to $kwdw, not $length"             # Check maximum length
    unless $length <= $kwdw;

  my $l2 = int($length/2);                                                      # Minimum number of keys in a node

  genHash(__PACKAGE__."::Tree",                                                 # Tree
    area                 => ($area // DescribeArea),                            # Area definition.
    length               => $length,                                            # Number of keys in a maximal block
    lengthLeft           => $l2,                                                # Left minimal number of keys
    lengthMiddle         => $l2 + 1,                                            # Number of splitting key counting from 1
    lengthMin            => $length - 1 - $l2,                                  # The smallest number of keys we are allowed in any node other than a root node.
    lengthOffset         => $keyAreaWidth,                                      # Offset of length in keys block.  The length field is a word - see: "MultiWayTree.svg"
    lengthRight          => $length - 1 - $l2,                                  # Right minimal number of keys
    loop                 => $b - $o,                                            # Offset of keys, data, node loop.
    maxKeys              => $length,                                            # Maximum number of keys allowed in this tree which might well ne less than the maximum we can store in a zmm.
    offset               => V(offset  => 0),                                    # Offset of last node found
    splittingKey         => ($l2 + 1) * $o,                                     # Offset at which to split a full block
    treeBits             => $keyAreaWidth + 2,                                  # Offset of tree bits in keys block.  The tree bits field is a word, each bit of which tells us whether the corresponding data element is the offset (or not) to a sub tree of this tree .
    treeBitsMask         => 0x3fff,                                             # Total of 14 tree bits
    keyDataMask          => 0x3fff,                                             # Key data mask
    name                 => $name,                                              # Optional name
    nodeMask             => 0x7fff,                                             # Node mask
    up                   => $keyAreaWidth,                                      # Offset of up in data block.
    width                => $o,                                                 # Width of a key or data slot.
    zWidth               => $b,                                                 # Width of a zmm register
    zWidthD              => $b / $o,                                            # Width of a zmm in double words being the element size
    maxKeysZ             => $b / $o - 2,                                        # The maximum possible number of keys in a zmm register
    maxNodesZ            => $b / $o - 1,                                        # The maximum possible number of nodes in a zmm register
    stringTree           => $stringTree // 0,                                   # String tree - now obsolete

    rootOffset           => $o * 0,                                             # Offset of the root field in the first block - the root field contains the offset of the block containing the keys of the root of the tree
    optionsOffset        => $o * 1,                                             # Offset of the options double word in the first block
    stringTreeBit        => 0,                                                  # Bit indicating string key tree
    sizeOffset           => $o * 2,                                             # Offset of the size field  in the first block - tells us the number of  keys in the tree
    fcControl            => $o * 3,                                             # Offset of the tree bits and present bits in the first cache of low key values for this tree.
    fcPresentOff         => $o * 3,                                             # Byte offset of word  containing present bits
    fcPresent            => 0,                                                  # Offset of the present bits in the control dword
    fcTreeBitsOff        => $o * 3 + RegisterSize(ax),                          # Byte offset of word containing tree bits
    fcTreeBits           => 16,                                                 # Offset of the tree bits in bits in the control dword
    fcArray              => $o * 4,                                             # Offset of cached array in first block
    fcDWidth             => $b / $o - 4,                                        # Number of dwords available in the first cache.  The first cache supplies an alternate area to store the values of keys less than this value  to fill the otherwise unused space in a way that improves the performance of trees when used to represent small arrays, stacks or structures.
    middleOffset         => $o * ($l2 + 0),                                     # Offset of the middle slot in bytes
    rightOffset          => $o * ($l2 + 1),                                     # Offset of the first right slot in bytes
    stringTreeCountOff   => $o * 4,                                             # This field is used to count the total number of keys in a string tree so that we can assign unique numbers when pushing.

    data                 => V('data   '),                                       # Variable containing the current data
    first                => V('first  '),                                       # Variable addressing offset to first block of the tree which is the header block
    found                => V('found  '),                                       # Variable indicating whether the last find was successful or not
    key                  => V('key    '),                                       # Variable containing the current key
    offset               => V('offset '),                                       # Variable containing the offset of the block containing the current key
    subTree              => V('subTree'),                                       # Variable indicating whether the last find found a sub tree

#    data                 => V('data   ' => 0),                                 # Variable containing the current data
#    first                => V('first  ' => 0),                                 # Variable addressing offset to first block of the tree which is the header block
#    found                => V('found  ' => 0),                                 # Variable indicating whether the last find was successful or not
#    key                  => V('key    ' => 0),                                 # Variable containing the current key
#    offset               => V('offset ' => 0),                                 # Variable containing the offset of the block containing the current key
#    subTree              => V('subTree' => 0),                                 # Variable indicating whether the last find found a sub tree
   );
 }

sub Nasm::X86::Area::DescribeTree($%)                                           #P Return a descriptor for a tree in the specified area with the specified options.
 {my ($area, %options) = @_;                                                    # Area descriptor, options for tree
  @_ >= 1 or confess;

  DescribeTree(area=>$area, %options)
 }

sub Nasm::X86::Area::CreateTree($%)                                             # Create a tree in an area.
 {my ($area, %options) = @_;                                                    # Area description, tree options
  @_ % 2 == 1 or confess "Odd number of parameters required";

  my $tree = $area->DescribeTree(%options);                                     # Return a descriptor for a tree in the specified area
  my $o    = $tree->area->allocZmmBlock;                                        # Allocate header
  $tree->first->copy($o);                                                       # Install header

  my ($z) = zmm 1;                                                              # Load options into first block
  my $t = $tree;
  my $a = $t->area;

  ClearRegisters rsi, $z;                                                       # At this point the first block is empty so there is no need to get it from memory because it has nothing in it.
  Bts rsi, $t->stringTreeBit if $t->stringTree;
  dRegIntoZmm rsi, $z, $t->optionsOffset;
  $a->putZmmBlock($o, $z);                                                      # Save first block with options loaded

  $tree                                                                         # Description of array
 }

sub Nasm::X86::Tree::DescribeTree($%)                                           #P Create a description of a tree.
 {my ($tree, %options) = @_;                                                    # Tree descriptor, {first=>first node of tree if not the existing first node; area=>area used by tree if not the existing area}
  @_ >= 1 or confess "At least one parameter";

  $tree->area->DescribeTree                                                     # Return a descriptor for a tree
   (stringTree => $tree->stringTree,
    %options
   );
 }

sub Nasm::X86::Tree::position($$)                                               #P Create a new tree description for a tree positioned at the specified location.
 {my ($tree, $first) = @_;                                                      # Tree descriptor, offset of tree
  my $t = $tree->DescribeTree;                                                  # Length of new tree must be same as old tree

  $t->first->copy($first);                                                      # Variable addressing offset to first block of keys.
  $t                                                                            # Return new descriptor
 }

sub Nasm::X86::Tree::cloneDescriptor($)                                         # Clone the descriptor of a tree to make a new tree descriptor.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";
  my $t = $tree->DescribeTree(length=>$tree->length);                           # Length of new tree must be same as old tree
  $t->copyDescriptor($tree);                                                    # Load new descriptor from original descriptor
  $t                                                                            # Return new descriptor
 }

sub Nasm::X86::Tree::copyDescriptor($$)                                         # Copy the description of one tree into another.
 {my ($target, $source) = @_;                                                   # The target of the copy, the source of the copy
  @_ == 2 or confess "Two parameters";
  $target->first->copy($source->first);                                         # Load the target first block from the source block
  $target->area->copyDescriptor($source->area);                                 # Load the target area description from the source area description
  $target                                                                       # Return target
 }

#sub Nasm::X86::Tree::down($)                                                    # Use the current B<find> result held in B<data> to position on the referenced subtree at the next level down.
# {my ($tree) = @_;                                                              # Tree descriptor which has just completed a successful find
#  @_ == 1 or confess "One parameter";
#  If $tree->data == 0,
#  Then
#   {PrintErrTraceBack "Invalid sub tree offset";
#   };
#  $tree->first->copy($tree->data);                                              # The next sub tree down is addressed by the B<data> field of the tree descriptor
#  $tree                                                                         # Return original descriptor
# }

#sub Nasm::X86::Tree::cloneAndDown($)                                            # Use the current B<find> result held in B<data> to position a new sub tree on the referenced subtree at the next level down.
# {my ($tree) = @_;                                                              # Tree descriptor which has just completed a successful find
#  @_ == 1 or confess "One parameter";
#  my $t = $tree->DescribeTree;                                                  # Clone the supplied tree
#  $t->first->copy($tree->data);                                                 # The next sub tree down is addressed by the B<data> field of the tree descriptor
#  $t                                                                            # Return original descriptor
# }

#sub Nasm::X86::Tree::copyDescription($)                                         #P Make a copy of a tree descriptor.
# {my ($tree) = @_;                                                              # Tree descriptor
#  my $t = $tree->DescribeTree;
#
#  $t->data   ->copy($tree->data );                                              # Variable containing the last data found
#  $t->first  ->copy($tree->first);                                              # Variable addressing offset to first block of keys.
#  $t->found  ->copy($tree->found);                                              # Variable indicating whether the last find was successful or not
#  $t->subTree->copy($tree->subTree);                                            # Variable indicating whether the last find found a sub tree
#  $t                                                                            # Return new descriptor
# }

sub Nasm::X86::Tree::firstFromMemory($$)                                        #P Load the first block for a tree into the numbered zmm.
 {my ($tree, $zmm) = @_;                                                        # Tree descriptor, number of zmm to contain first block
  @_ == 2 or confess "Two parameters";
  my $base = rdi; my $offset = rsi;
  $tree->area->address->setReg($base);
  $tree->first->setReg($offset);
  Vmovdqu64 zmm($zmm), "[$base+$offset]";
 }

sub Nasm::X86::Tree::firstIntoMemory($$)                                        #P Save the first block of a tree in the numbered zmm back into memory.
 {my ($tree, $zmm) = @_;                                                        # Tree descriptor, number of zmm containing first block
  @_ == 2 or confess "Two parameters";
  my $base = rdi; my $offset = rsi;
  $tree->area->address->setReg($base);
  $tree->first->setReg($offset);
  Vmovdqu64  "[$base+$offset]", zmm($zmm);
 }

sub Nasm::X86::Tree::rootIntoFirst($$$)                                         #P Put the contents of a variable into the root field of the first block of a tree when held in a zmm register.
 {my ($tree, $zmm, $value) = @_;                                                # Tree descriptor, number of zmm containing first block, variable containing value to put
  @_ == 3 or confess "Three parameters";
  $value->dIntoZ($zmm, $tree->rootOffset);
 }

sub Nasm::X86::Tree::rootFromFirst($$%)                                         #P Return a variable containing the offset of the root block of a tree from the first block when held in a zmm register.
 {my ($tree, $zmm, %options) = @_;                                              # Tree descriptor, number of zmm containing first block, options
  @_ >= 2 or confess "Two or more parameters";

  dFromZ $zmm, $tree->rootOffset, %options;
 }

sub Nasm::X86::Tree::root($$$)                                                  #P Check whether the specified offset refers to the root of a tree when the first block is held in a zmm register. The result is returned by setting the zero flag to one if the offset is the root, else to zero.
 {my ($t, $F, $offset) = @_;                                                    # Tree descriptor, zmm register holding first block, offset of block as a variable
  @_ == 3 or confess "Three parameters";
  my $root = $t->rootFromFirst($F);                                             # Get the offset of the corresponding data block
  $root == $offset                                                              # Check whether the offset is in fact the root
 }


sub Nasm::X86::Tree::optionsFromFirst($$%)                                      #P Return a variable containing the options double word from the first block zmm register.
 {my ($tree, $zmm, %options) = @_;                                              # Tree descriptor, number of zmm containing first block, options
  @_ >= 2 or confess "Two or more parameters";
  dFromZ $zmm, $tree->optionsOffset;
 }

sub Nasm::X86::Tree::optionsIntoFirst($$$%)                                     #P Put the contents of a variable into the options field of the first block of a tree  when the first block is held in a zmm register.
 {my ($tree, $zmm, $value, %options) = @_;                                      # Tree descriptor, number of zmm containing first block, variable containing options to put, options
  @_ >= 3 or confess "Three or more parameters";
  $value->dIntoZ($zmm, $tree->optionsOffset);
 }


sub Nasm::X86::Tree::sizeFromFirst($$)                                          #P Return a variable containing the number of keys in the specified tree when the first block is held in a zmm register..
 {my ($tree, $zmm) = @_;                                                        # Tree descriptor, number of zmm containing first block
  @_ == 2 or confess "Two parameters";
  dFromZ $zmm, $tree->sizeOffset;
 }

sub Nasm::X86::Tree::sizeIntoFirst($$$)                                         #P Put the contents of a variable into the size field of the first block of a tree  when the first block is held in a zmm register.
 {my ($tree, $zmm, $value) = @_;                                                # Tree descriptor, number of zmm containing first block, variable containing value to put
  @_ == 3 or confess "Three parameters";
  $value->dIntoZ($zmm, $tree->sizeOffset);
 }

sub Nasm::X86::Tree::incSizeInFirst($$)                                         #P Increment the size field in the first block of a tree when the first block is held in a zmm register.
 {my ($tree, $zmm) = @_;                                                        # Tree descriptor, number of zmm containing first block
  @_ == 2 or confess "Two parameters";
  my $o = $tree->sizeOffset;
  my $w = RegisterSize zmm0;
  Vmovdqu64 "[rsp-$w]", zmm $zmm;                                               # Position below stack
  Inc "dword[rsp-$w+$o]";                                                       # Increment size field
  Vmovdqu64 zmm($zmm), "[rsp-$w]";                                              # Reload from stack
 }

sub Nasm::X86::Tree::decSizeInFirst($$)                                         #P Decrement the size field in the first block of a tree when the first block is held in a zmm register.
 {my ($tree, $zmm) = @_;                                                        # Tree descriptor, number of zmm containing first block
  @_ == 2 or confess "Two parameters";
  my $o = $tree->sizeOffset;
  my $w = RegisterSize xmm0;
  Vmovdqu64 "[rsp-$w]", zmm $zmm;                                               # Position below stack

  if ($DebugMode)
   {Cmp "dword[rsp-$w+$o]", 0;                                                  # Check size is not already zero
    IfEq
    Then
     {PrintErrTraceBack "Cannot decrement zero length tree";
     };
   }

  Dec "dword[rsp-$w+$o]";                                                       # Decrement size field
  Vmovdqu64 zmm($zmm), "[rsp-$w]";                                              # Reload from stack
 }

sub Nasm::X86::Tree::incSize($)                                                 #P Increment the size of a tree.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";
  $tree->firstFromMemory(1);
  $tree->incSizeInFirst (1);
  $tree->firstIntoMemory(1);
 }

sub Nasm::X86::Tree::decSize($)                                                 #P Decrement the size of a tree.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";
  $tree->firstFromMemory(1);
  $tree->decSizeInFirst (1);
  $tree->firstIntoMemory(1);
 }

sub Nasm::X86::Tree::size($)                                                    # Return in a variable the number of elements currently in the tree.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";
  my $F = zmm1;
  $tree->firstFromMemory($F);
  my $s = $tree->sizeFromFirst($F);
  $s->name = q(size of tree);
  $s
 }

sub Nasm::X86::Tree::allocBlock($$$$)                                           #P Allocate a keys/data/node block and place it in the numbered zmm registers.
 {my ($tree, $K, $D, $N) = @_;                                                  # Tree descriptor, numbered zmm for keys, numbered zmm for data, numbered zmm for children
  @_ == 4 or confess "Four parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $t = $$s{tree};                                                          # Tree
    my $a = $t->area;                                                           # Area

    my ($k, $d, $n) = $a->allocZmmBlock3;                                       # Keys, data, children

    $t->putLoop($d, $K);                                                        # Set the link from key to data
    $t->putLoop($n, $D);                                                        # Set the link from data to node
    $t->putLoop($t->first, $N);                                                 # Set the link from node to tree first block
    $$p{address}->copy($k);                                                     # Address of block
   } structures => {tree => $tree},
     parameters => [qw(address)],
     name       =>
     qq(Nasm::X86::Tree::allocBlock-${K}-${D}-${N}-$$tree{length});             # Create a subroutine for each combination of registers encountered

  $s->inline
   (structures => {tree => $tree},
    parameters => {address =>  my $a = V address => 0});

  $a
 } # allocBlock

sub Nasm::X86::Tree::freeBlock($$$$$)                                           #P Free a keys/data/node block whose keys  block entry is located at the specified offset.
 {my ($tree, $k, $K, $D, $N) = @_;                                              # Tree descriptor, offset of keys block, numbered zmm for keys, numbered zmm for data, numbered zmm for children
  @_ == 5 or confess "Five parameters";
  my $d = $tree->getLoop($K);
  my $n = $tree->getLoop($D);

  $tree->area->freeZmmBlock($_) for $k, $d, $n;                                 # Free the component zmm blocks
 } # freeBlock

sub Nasm::X86::Tree::upFromData($$%)                                            #P Up from the data zmm in a block in a tree.
 {my ($tree, $zmm, %options) = @_;                                              # Tree descriptor, number of zmm containing data block, options
  @_ >= 2 or confess "Two or more parameters";
  dFromZ $zmm, $tree->up, %options;
 }

sub Nasm::X86::Tree::upIntoData($$$)                                            #P Up into the data zmm in a block in a tree.
 {my ($tree, $value, $zmm) = @_;                                                # Tree descriptor, variable containing value to put, number of zmm containing first block
  @_ == 3 or confess "Three parameters";
  $value->dIntoZ($zmm, $tree->up);
 }

sub Nasm::X86::Tree::lengthFromKeys($$%)                                        #P Get the length of the keys block in the numbered zmm and return it as a variable.
 {my ($t, $zmm, %options) = @_;                                                 # Tree descriptor, zmm number, options
  @_ >= 2 or confess "Two or more parameters";

  bFromZ($zmm, $t->lengthOffset, %options);                                     # The length field as a variable
 }

sub Nasm::X86::Tree::lengthIntoKeys($$$)                                        #P Get the length of the block in the numbered zmm from the specified variable.
 {my ($t, $zmm, $length) = @_;                                                  # Tree, zmm number, length variable
  @_ == 3 or confess "Three parameters";
  ref($length) or confess dump($length);
  $length->bIntoZ($zmm, $t->lengthOffset)                                       # Set the length field
 }

sub Nasm::X86::Tree::incLengthInKeys($$)                                        #P Increment the number of keys in a keys block or complain if such is not possible.
 {my ($t, $K) = @_;                                                             # Tree, zmm number
  @_ == 2 or confess "Two parameters";
  my $l = $t->lengthOffset;                                                     # Offset of length bits
  my $w = RegisterSize zmm0;
  Vmovdqu64 "[rsp-$w]", zmm$K;                                                  # Position below stack

  if ($DebugMode)                                                               # With checking
   {Cmp "byte[rsp-$w+$l]", $t->length;
    IfGe
    Then
     {PrintErrTraceBack "Cannot increment length of block beyond ".$t->length;
     };
   }

  Inc "byte[rsp-$w+$l]";                                                        # Increment size field
  Vmovdqu64 zmm($K), "[rsp-$w]";                                                # Reload from stack
 }

sub Nasm::X86::Tree::decLengthInKeys($$)                                        #P Decrement the number of keys in a keys block or complain if such is not possible.
 {my ($t, $K) = @_;                                                             # Tree, zmm number
  @_ == 2 or confess "Two parameters";
  my $l = $t->lengthOffset;                                                     # Offset of length bits
  my $w = RegisterSize zmm0;
  Vmovdqu64 "[rsp-$w]", zmm$K;                                                  # Position below stack

  if ($DebugMode)                                                               # With checking
   {Cmp "byte[rsp-$w+$l]", 0;
    IfLe
    Then
     {PrintErrTraceBack "Cannot decrement length of block below zero";
     };
   }

  Dec "byte[rsp-$w+$l]";                                                        # Decrement size field
  Vmovdqu64 zmm($K), "[rsp-$w]";                                                # Reload from stack
 }

sub Nasm::X86::Tree::leafFromNodes($$%)                                         #P Return a variable containing true if we are on a leaf.  We determine whether we are on a leaf by checking the offset of the first sub node.  If it is zero we are on a leaf otherwise not.
 {my ($tree, $zmm, %options) = @_;                                              # Tree descriptor, number of zmm containing node block, options
  my %opt = %options;
  my $set = delete $opt{set};                                                   # Register version
  confess "Invalid options: ".join(", ", sort keys %options) if keys %opt;      # Complain about any invalid options

  @_ >= 2 or confess "Two or more parameters";
  if ($set)                                                                     # Register version
   {dFromZ $zmm, 0, %options;
   }
  else                                                                          # Variable version
   {my $n = dFromZ $zmm, 0;                                                     # Get first node
    my $l = V leaf => 0;                                                        # Return a variable which is non zero if  this is a leaf
    If $n == 0, Then {$l->copy(1)};                                             # Leaf if the node is zero
    return $l                                                                   # Variable containing result
   }
 }

sub Nasm::X86::Tree::getLoop($$%)                                               #P Return the value of the loop field as a variable.
 {my ($t, $zmm, %options) = @_;                                                 # Tree descriptor, numbered zmm, options
  @_ >= 2 or confess "Two or more parameters";
  dFromZ $zmm, $t->loop, %options;                                              # Get loop field as a variable
 }

sub Nasm::X86::Tree::putLoop($$$)                                               #P Set the value of the loop field from a variable.
 {my ($t, $value, $zmm) = @_;                                                   # Tree descriptor, variable containing offset of next loop entry, numbered zmm
  @_ == 3 or confess "Three parameters";
  $value->dIntoZ($zmm, $t->loop);                                               # Put loop field as a variable
 }

sub Nasm::X86::Tree::maskForFullKeyArea                                         #P Place a mask for the full key area in the numbered mask register.
 {my ($tree, $maskRegister) = @_;                                               # Tree description, mask register
  my $m = registerNameFromNumber $maskRegister;
  ClearRegisters $m;                                                            # Zero register
  Knotq $m, $m;                                                                 # Invert to fill with ones
  Kshiftrw $m, $m, 2;                                                           # Mask with ones in the full key area
 }

sub Nasm::X86::Tree::maskForFullNodesArea                                       #P Place a mask for the full nodes area in the numbered mask register.
 {my ($tree, $maskRegister) = @_;                                               # Tree description, mask register
  my $m = registerNameFromNumber $maskRegister;
  ClearRegisters $m;                                                            # Zero register
  Knotq $m, $m;                                                                 # Invert to fill with ones
  Kshiftrw $m, $m, 1;                                                           # Mask with ones in the full key area
 }

sub Nasm::X86::Tree::getBlock($$$$$)                                            #P Get the keys, data and child nodes for a tree node from the specified offset in the area for the tree.
 {my ($tree, $offset, $K, $D, $N) = @_;                                         # Tree descriptor, offset of block as a variable, numbered zmm for keys, numbered data for keys, numbered zmm for nodes
  @_ == 5 or confess "Five parameters";
  my $a = $tree->area;                                                          # Underlying area

  if (ref($offset) =~ m(Variable))                                              # Using variables
   {$a->getZmmBlock($offset,  $K);                                              # Get the keys block
    my $data = $tree->getLoop($K);                                              # Get the offset of the corresponding data block
    $a->getZmmBlock($data,    $D);                                              # Get the data block
    my $node = $tree->getLoop($D);                                              # Get the offset of the corresponding node block
    $a->getZmmBlock($node,    $N);                                              # Get the node block
   }
  else                                                                          # Using registers
   {my $A = rsi; my $O = rdi;
    $a->address->setReg($A);

    Vmovdqu64 zmm($K), "[$A+$offset]";                                          # Read keys from memory
    Mov edi, "[$A+$offset+$$tree{loop}]";                                       # Loop offset

    Vmovdqu64 zmm($D), "[$A+rdi]";                                              # Read data from memory
    Mov edi, "[$A+rdi+$$tree{loop}]";                                           # Loop nodes offset

    Vmovdqu64 zmm($N), "[$A+rdi]";                                              # Read from memory
   }
 }

sub Nasm::X86::Tree::putBlock($$$$$)                                            #P Put a tree block held in three zmm registers back into the area holding the tree at the specified offset.
 {my ($t, $offset, $K, $D, $N) = @_;                                            # Tree descriptor, offset of block as a variable, numbered zmm for keys, numbered data for keys, numbered zmm for nodes
  @_ == 5 or confess "Five parameters";
  my $a    = $t->area;                                                          # Area for tree
  $a->putZmmBlock($offset, $K);                                                 # Put the keys block
  $t->getLoop(  $K, set=>rsi);                                                  # Get the offset of the corresponding data block
  $a->putZmmBlock(rsi,   $D);                                                   # Put the data block
  $t->getLoop(  $D, set=>rsi);                                                  # Get the offset of the corresponding node block
  $a->putZmmBlock(rsi,   $N);                                                   # Put the node block
 }

sub Nasm::X86::Tree::firstNode($$$$)                                            #P Return as a variable the last node block in the specified tree node held in a zmm.
 {my ($tree, $K, $D, $N) = @_;                                                  # Tree definition, key zmm, data zmm, node zmm for a node block
  @_ == 4 or confess "Four parameters";

  dFromZ($N, 0)
 }

sub Nasm::X86::Tree::lastNode($$$$)                                             #P Return as a variable the last node block in the specified tree node held in a zmm.
 {my ($tree, $K, $D, $N) = @_;                                                  # Tree definition, key zmm, data zmm, node zmm for a node block
  @_ == 4 or confess "Four parameters";

  dFromZ($N, $tree->lengthFromKeys($K) * $tree->width)
 }

sub Nasm::X86::Tree::relativeNode($$$$$)                                        #P Return as a variable a node offset relative (specified as ac constant) to another offset in the same node in the specified zmm.
 {my ($tree, $offset, $relative, $K, $N) = @_;                                  # Tree definition, offset, relative location, key zmm, node zmm
  @_ == 5 or confess "Five parameters";

  abs($relative) == 1 or confess "Relative must be +1 or -1";

  my $l = $tree->lengthFromKeys($K);                                            # Length of block
  PushR $K, 7, 15;                                                              # Reuse keys for comparison value
  $offset->setReg(15);
  Vpbroadcastd zmm($K), r15d;                                                   # Load offset to test
  Vpcmpud k7, zmm($N, $K), $Vpcmp->eq;                                          # Check for nodes equal to offset
  Kmovq r15, k7;
  Tzcnt r15, r15;                                                               # Index of offset

  if ($relative < 0)
   {if ($DebugMode)                                                             # With checking
     {Cmp r15, 0;
      IfEq Then{PrintErrTraceBack "Cannot get offset before first offset"};
     }
    Sub r15, 1;                                                                 # Set flags
   }
  if ($relative > 0)
   {if ($DebugMode)                                                             # With checking
     {Cmp r15, $tree->length;
      IfGt Then{PrintErrTraceBack "Cannot get offset beyond last offset"};
     }
    Add r15, 1;                                                                 # Set flags
   }
  my $r = dFromZ $N, V(offset => r15) * $tree->width;                           # Select offset
  PopR;

  $r
 }

sub Nasm::X86::Tree::nextNode($$$$)                                             #P Return as a variable the next node block offset after the specified one in the specified zmm.
 {my ($tree, $offset, $K, $N) = @_;                                             # Tree definition, offset, key zmm, node zmm
  @_ == 4 or confess "Four parameters";
  $tree->relativeNode($offset, +1, $K, $N);
 }

sub Nasm::X86::Tree::prevNode($$$$)                                             #P Return as a variable the previous node block offset after the specified one in the specified zmm.
 {my ($tree, $offset, $K, $N) = @_;                                             # Tree definition, offset, key zmm, node zmm
  @_ == 4 or confess "Four parameters";
  $tree->relativeNode($offset, -1, $K, $N);
 }

sub Nasm::X86::Tree::indexNode($$$$)                                            #P Return, as a variable, the point mask obtained by testing the nodes in a block for specified offset. We have to supply the keys as well so that we can find the number of nodes. We need the number of nodes so that we only search the valid area not all possible node positions in the zmm.
 {my ($tree, $offset, $K, $N) = @_;                                             # Tree definition, key as a variable, zmm containing keys, comparison from B<Vpcmp>
  @_ == 4 or confess "Four parameters";
  my $l = $tree->lengthFromKeys($K);                                            # Current length of the keys block

  my $A = $K == 1 ? 0 : 1;                                                      # The broadcast facility 1 to 16 does not seem to work reliably so we load an alternate zmm

  $offset->setReg(rdi);                                                         # The offset we are looking for
  Vpbroadcastd zmm($A), edi;                                                    # Load offset to test
  Vpcmpud k1, zmm($N, $A), $Vpcmp->eq;                                          # Check for nodes equal to offset
  $l->setReg(rcx);                                                              # Create a mask of ones that matches the width of a key node in the current tree.
  Mov   rsi, 2;                                                                 # A one in position two because the number of nodes is always one more than the number of keys
  Shl   rsi, cl;                                                                # Position the one at end of nodes block
  Dec   rsi;                                                                    # Reduce to fill block with ones
  Kmovq rdi, k1;                                                                # Matching nodes
  And   rsi, rdi;                                                               # Matching nodes in mask area
  V index => rsi;                                                               # Save result as a variable
 }

sub Nasm::X86::Tree::expand($$)                                                 #P Expand the node at the specified offset in the specified tree if it needs to be expanded and is not the root node (which cannot be expanded because it has no siblings to take substance from whereas as all other nodes do).  Set tree.found to the offset of the left sibling if the node at the specified offset was merged into it and freed else set tree.found to zero.
 {my ($tree, $offset) = @_;                                                     # Tree descriptor, offset of node block to expand
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      PushR 8..15, 22..31;

      my $t = $$s{tree};                                                        # Tree to search
      my $L = $$p{offset};                                                      # Offset of node to expand is currently regarded as left
      my $F = 31;
      my $PK = 30; my $PD = 29; my $PN = 28;
      my $LK = 27; my $LD = 26; my $LN = 25;
      my $RK = 24; my $RD = 23; my $RN = 22;

      $t->found->copy(0);                                                       # Assume the left node will not be freed by the expansion
      $t->firstFromMemory($F);                                                  # Load first block
      my $root = $t->rootFromFirst($F);                                         # Root node block offset
      If $root == 0 || $root == $L, Then {Jmp $success};                        # Empty tree or on root so nothing to do

      Block                                                                     # If not on the root and node has the minimum number of keys then either steal left or steal right or merge left or merge right
       {my ($end, $start) = @_;                                                 # Code with labels supplied
        $t->getBlock($L, $LK, $LD, $LN);                                        # Load node from memory
        my $ll = $t->lengthFromKeys($LK);                                       # Length of node
        If $ll > $t->lengthMin, Then {Jmp $end};                                # Has more than the bare minimum so does not need to be expanded

        my $P = $t->upFromData($LD);                                            # Parent offset
        $t->getBlock($P, $PK, $PD, $PN);                                        # Get the parent keys/data/nodes
        my $fn = $t->firstNode($PK, $PD, $PN);                                  # Parent first node
        my $ln = $t-> lastNode($PK, $PD, $PN);                                  # Parent last node

        my $R = V right => 0;                                                   # The node on the right
        my $plp = $t->indexNode($L, $PK, $PN);                                  # Position of the left node in the parent

        if ($DebugMode)                                                         # With checking
         {If $plp == 0,                                                         # Zero implies that the left child is not registered in its parent
          Then
           {PrintErrTraceBack "Cannot find left node in parent";
           };
         }

        If $L == $ln,                                                           # If necessary step one to the let and record the fact that we did is that we can restart the search at the top
        Then                                                                    # Last child and needs merging
         {Vmovdqu64 zmm $RK, $LK;                                               # Copy the current left node into the right node
          Vmovdqu64 zmm $RD, $LD;
          Vmovdqu64 zmm $RN, $LN;
          $R->copy($L);                                                         # Left becomes right node because it is last
          my $l = $plp >> K(one => 1);                                          # The position of the previous node known to exist because we are currently on the last node
          $L->copy($l->dFromPointInZ($PN));                                     # Load previous sibling as new left keeping old left in right so that left and right now form a pair of siblings
          $t->getBlock($L, $LK, $LD, $LN);                                      # Load the new left
          $t->found->copy($L);                                                  # Show that we created a new left
         },
        Else
         {my $r = $plp << K(one => 1);                                          # The position of the node to the right known to exist because we are not currently on the last node
          $R->copy($r->dFromPointInZ($PN));                                     # Load next sibling as right
          $t->getBlock($R, $RK, $RD, $RN);                                      # Load the right sibling
         };

        my $lr = $t->lengthFromKeys($RK);                                       # Length of right
        If $lr == $t->lengthMin,
        Then                                                                    # Merge left and right into left as they are both at minimum size
         {$t->merge($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);               # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm.
          $t->freeBlock($R, $RK, $RD, $RN);                                     # The right is no longer required because it has been merged away

          my $lp = $t->lengthFromKeys($PK);                                     # New length of parent
          If $lp == 0,
          Then                                                                  # Root now empty
           {$t->rootIntoFirst($F, $L);                                          # Parent is now empty so the left block must be the new root
            $t->firstIntoMemory($F);                                            # Save first block with updated root
            $t->freeBlock($P, $PK, $PD, $PN);                                   # The parent is no longer required because the left node s the new root
           },
          Else                                                                  # Root not empty
           {$t->putBlock($P, $PK, $PD, $PN);                                    # Write parent back into memory
           };
         },
        Else                                                                    # Steal from right as it is too big to merge and so must have some excess that we can steal
         {$t->stealFromRight($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);      # Steal
          $t->putBlock($P, $PK, $PD, $PN);                                      # Save modified parent
          $t->putBlock($R, $RK, $RD, $RN);                                      # Save modified right
         };
        $t->putBlock($L, $LK, $LD, $LN);                                        # Save non minimum left

        If $t->leafFromNodes($LN) == 0,
        Then                                                                    # Not a leaf
         {PushR $RK, $RD, $RN;                                                  # Save these zmm even though we are not going to need them any more
          ($t->lengthFromKeys($LK) + 1)->for(sub                                # Reparent the children of the left hand side.  This is not efficient as we load all the children (if there are any) but it is effective.
           {my ($index, $start, $next, $end) = @_;
            my $R = dFromZ $LN, $index * $t->width;                             # Offset of node
            $t->getBlock  ($R, $RK, $RD, $RN);                                  # Get child of right node reusing the left hand set of registers as we no longer need them having written them to memory
            $t->upIntoData($L,      $RD);                                       # Parent for child of right hand side
            $t->putBlock  ($R, $RK, $RD, $RN);                                  # Save block into memory now that its parent pointer has been updated
           });
           PopR;
         };
       };  # Block
     }; # Block                                                                 # Find completed successfully
    PopR;
   } parameters=>[qw(offset)],
     structures=>{tree=>$tree},
     name => qq(Nasm::X86::Tree::expand-$$tree{length});

  $s->call(structures=>{tree => $tree}, parameters=>{offset => $offset});
 } # expand

sub Nasm::X86::Tree::replace($$$$)                                              #P Replace the key/data/subTree at the specified point in the specified zmm with the values found in the tree key/data/sub tree fields.
 {my ($tree, $point, $K, $D) = @_;                                              # Tree descriptor, point at which to extract, keys zmm, data zmm
  @_ == 4 or confess "Four parameters";

  $point->dIntoPointInZ($K, $tree->key);                                        # Key
  $point->dIntoPointInZ($D, $tree->data);                                       # Data at point

  $tree->setOrClearTreeBitToMatchContent($K, $point, $tree->subTree);           # Replace tree bit
 } # replace

sub Nasm::X86::Tree::overWriteKeyDataTreeInLeaf($$$$$$$)                        #P Over write an existing key/data/sub tree triple in a set of zmm registers and set the tree bit as indicated.
 {my ($tree, $point, $K, $D, $IK, $ID, $subTree) = @_;                          # Tree descriptor, register point at which to overwrite formatted as a one in a sea of zeros, key, data, insert key, insert data, sub tree if tree.

  @_ == 7 or confess "Seven parameters";

  Kmovq k1, $point;                                                             # A sea of zeros with a one at the point of insertion

  $IK->setReg(rdi); Vpbroadcastd zmmM($K, 1), edi;                              # Insert value at expansion point
  $ID->setReg(rdi); Vpbroadcastd zmmM($D, 1), edi;

  If $subTree > 0,                                                              # Set the inserted tree bit
  Then
   {Kmovq rdi, k1;
    $tree->setTreeBit ($K, rdi);
   },
  Else
   {Kmovq rdi, k1;
    $tree->clearTreeBit($K, rdi);
   };
 } # overWriteKeyDataTreeInLeaf

#D2 Insert                                                                      # Insert a key into the tree.

sub Nasm::X86::Tree::indexXX($$$$$%)                                            #P Return, as a variable, the mask obtained by performing a specified comparison on the key area of a node against a specified key.
 {my ($tree, $key, $K, $cmp, $inc, %options) = @_;                              # Tree definition, key to search for as a variable or a zmm containing a copy of the key to be searched for in each slot, zmm containing keys, comparison from B<Vpcmp>, whether to increment the result by one, options
  @_ >= 5 or confess "Five or more parameters";

  my $r = $options{set} // rsi;                                                 # Target register supplied or implied
  confess "Cannot use rdi as a target:" if $r eq rdi;

  $tree->lengthFromKeys($K, set=>rdi);                                          # Current length of the keys block
  my $masks = Rq(map {2**$_ -1} 0..15);                                         # Mask for each length
  Mov $r, "[$masks+rdi*8]";                                                     # Load mask address

  my $A = sub                                                                   # Zmm containing key to test
   {return $key unless ref $key;                                                # Zmm already contains keys
    my $A = $K == 1 ? 0 : 1;                                                    # Choose a free zmm to load the keys into
    $key->setReg(rdi);                                                          # Set key
    Vpbroadcastd zmm($A), edi;                                                  # Load key to test
    $A                                                                          # Return zmm loaded with key to test
   }->();

  Vpcmpud k1, zmm($K, $A), $cmp;                                                # Check keys from memory broadcast
  Kmovq rdi, k1;                                                                # Matching keys
  And   $r, rdi;                                                                # Matching keys in mask area
  Add   $r, 1 if $inc;                                                          # Add sets flags whereas Inc would not
  V index => rsi unless $options{set};                                          # Save result as a variable unless a target register has been supplied
 }

sub Nasm::X86::Tree::indexEq($$$%)                                              #P Return the  position of a key in a zmm equal to the specified key as a point in a variable.
 {my ($tree, $key, $K, %options) = @_;                                          # Tree definition, key as a variable, zmm containing keys, options
  @_ >= 3 or confess "Three parameters";

  $tree->indexXX($key, $K, $Vpcmp->eq, 0, %options);                            # Check for equal keys from the broadcasted memory
 }

sub Nasm::X86::Tree::insertionPoint($$$%)                                       #P Return the position at which a key should be inserted into a zmm as a point in a variable.
 {my ($tree, $key, $K, %options) = @_;                                          # Tree definition, key as a variable, zmm containing keys, options
  @_ >= 3 or confess "Three or more parameters";

  $tree->indexXX($key, $K, $Vpcmp->le, 1, %options);                            # Check for less than or equal keys
 }

sub Nasm::X86::Tree::indexEqLt($$$$$)                                           #P Set the specified registers with the equals point and the insertion point for the specified key in the specified zmm.
 {my ($tree, $key, $K, $setEq, $setLt) = @_;                                    # Tree definition, zmm containing a copy of the key to be searched for in each slot, zmm to check, bound register to set with equals point, bound register to set with insertion point.
  @_ == 5 or confess "Five parameters";

  confess "Cannot use rdi as a target register" if $setEq =~ m(\Ardi\Z);        # Check the target registers against our work register
  confess "Cannot use rdi as a target register" if $setLt =~ m(\Ardi\Z);

  $tree->lengthFromKeys($K, set=>rdi);                                          # Current length of the keys block
  my $masks = Rq(map {2**$_ -1} 0..15);                                         # Mask for each length
  Mov $setEq, "[$masks+rdi*8]";                                                 # Load mask address
  Mov $setLt, $setEq;

  if ($tree->stringTree)                                                        # Use binary search if we are processing a string tree
   {my $size = $tree->lengthFromKeys($K);

    BinarySearchD                                                               # Search
      size    => sub {my ($r) = @_; $size->setReg($r)},                         # Number of keys in this block
      found   => sub
       {Mov $setEq, 0;
        Bts $setEq, rax;                                                        # Show found position
        Cmp $setEq, 0;                                                          # Show found
       },
      after   => sub
       {Mov $setEq, 0;                                                          # Not found
        Mov $setLt, 0;                                                          # Greater than all keys
        $size->setReg(rsi);                                                     # Number of keys in block
        Bts $setLt, rsi;                                                        # Set insertion point at end
        Cmp $setEq, 0;                                                          # Show not found
       },
      before  => sub
       {Mov $setEq, 0;                                                          # Not found
        Mov $setLt, 1;                                                          # Less than all keys
        Cmp $setEq, 0;                                                          # Show not found
       },
      between => sub
       {Mov $setEq, 0;                                                          # Not found
        Mov $setLt, 0;
        Inc rax;
        Bts $setLt, rax;
        Cmp $setEq, 0;                                                          # Show not found
       },
      compare => sub                                                            # Compare
       {my ($index) = @_;                                                       # Search key in register, current index in register
        my $W = RegisterSize(zmm1);                                             # Size of a zmm register
        Vmovdqu64 "[rsp-$W]", $K;                                               # Push below stack
        Mov esi, "[rsp-$W+$index*4]";                                           # Offset of indexed key in area
        $tree->area->address->setReg(rdi);                                      # Address of area
        Vpcmpub k1, $key, "[rdi+rsi]", $Vpcmp->ne;                              # Not equal so if they are equal we get zeros which can be counted
        Kmovq rdx, k1;                                                          # Not equal mask
        Cmp rdx, 0;                                                             # Set the flags to show the two keys are equals
        IfNe
        Then                                                                    # The two key strings are not equal
         {Vpcmpub k2, $key, "[rdi+rsi]", $Vpcmp->gt;
          Kmovq rdi, k2;                                                        # Greater than mask
          Tzcnt rdx, rdx;                                                       # First quad at which the zmm registers differ
          Bt rdi, rdx;                                                          # Test the next bit to determine greater than or less than
          IfC
          Then                                                                  # First byte that differs is less than the key
           {Mov rsi, 1;
            Cmp rsi, 0;                                                         # Show  key being searched for is lower than indexed key
           },
          Else                                                                  # First quad that differs is less than the key
           {Mov rsi, 0;
            Cmp rsi, 1;                                                         # Show  key being searched for is greater than indexed key
           };
         };
       };
   }
  else                                                                          # Normal comparison
   {Vpcmpud k1, zmm($K, $key), $Vpcmp->eq;                                      # Check for equality  point
    Vpcmpud k2, zmm($K, $key), $Vpcmp->le;                                      # Check for insertion point
    Kmovq rdi, k2;                                                              # Insertion leading
    And   $setLt, rdi;                                                          # Matching keys in mask area
    Inc   $setLt;                                                               # Insertion point
    Kmovq rdi, k1;                                                              # Equality point
    And   $setEq, rdi;                                                          # Matching keys in mask area setting flags for fast test
   };
 }

sub Nasm::X86::Tree::insertKeyDataTreeIntoLeaf($$$$$$$$)                        #P Insert a new key/data/sub tree triple into a set of zmm registers if there is room, increment the length of the node and set the tree bit as indicated and increment the number of elements in the tree.
 {my ($tree, $point, $F, $K, $D, $IK, $ID, $subTree) = @_;                      # Tree descriptor, register point at which to insert formatted as a one in a sea of zeros, first, key, data, insert key, insert data, sub tree if tree.

  @_ == 8 or confess "Eight parameters";
  my $t = $tree;                                                                # Address tree

  Kmovq k3, $point;                                                             # A sea of zeros with a one at the point of insertion
  $t->maskForFullKeyArea(2);                                                    # Mask for key area
  Kandnq  k1, k3, k2;                                                           # Mask for key area with a hole at the insertion point

  Vpexpandd zmmM($K, 1), zmm($K);                                               # Expand to make room for the value to be inserted
  Vpexpandd zmmM($D, 1), zmm($D);

  $IK->setReg(rdi); Vpbroadcastd zmmM($K, 3), edi;                              # Insert value at expansion point
  $ID->setReg(rdi); Vpbroadcastd zmmM($D, 3), edi;

  $t->incLengthInKeys($K);                                                      # Increment the length of this node to include the inserted value

  $t->insertIntoTreeBits($K, 3, $subTree);                                      # Set the matching tree bit depending on whether we were supplied with a tree or a variable

  $t->incSizeInFirst($F);                                                       # Update number of elements in entire tree.
 } # insertKeyDataTreeIntoLeaf

sub Nasm::X86::Tree::splitNode($$)                                              #P Split a node if it it is full returning a variable that indicates whether a split occurred or not.
 {my ($tree, $offset) = @_;                                                     # Tree descriptor,  offset of block in area of tree as a variable
  @_ == 2 or confess 'Two parameters';

  my $PK = 11;  my $PD = 2; my $PN = 3;                                         # Key, data, node blocks. L<insertionPoint> uses zmm registers 0 and 1 so we cannot use those registers
  my $LK =  4;  my $LD = 5; my $LN = 6;
  my $RK =  7;  my $RD = 8; my $RN = 9;
  my $F  = 10;
                                                                                # First block of this tree
  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      my $t    = $$s{tree};                                                     # Tree
      my $area = $t->area;                                                      # Area

      ClearRegisters $PD;                                                       # Otherwise we get left over junk

      my $offset = $$p{offset};                                                 # Offset of block in area
      my $split  = $$p{split};                                                  # Indicate whether we split or not
      $t->getBlock($offset, $LK, $LD, $LN);                                     # Load node as left

      $t->lengthFromKeys($LK, set => rsi);
      Cmp rsi, $t->maxKeys;
      IfLt
      Then                                                                      # Only split full blocks
       {$split->copy(K split => 0);                                             # Split not required
        Jmp $success;
       };

      my $parent = $t->upFromData($LD);                                         # Parent of this block

      my $r = $t->allocBlock    ($RK, $RD, $RN);                                # Create a new right block
      If $parent > 0,
      Then                                                                      # Not the root node because it has a parent
       {$t->upIntoData      ($parent, $RD);                                     # Address existing parent from new right
        $t->getBlock        ($parent, $PK, $PD, $PN);                           # Load extent parent
        $t->splitNotRoot    ($r,      $PK, $PD, $PN, $LK,$LD,$LN, $RK,$RD,$RN);
        $t->putBlock        ($parent, $PK, $PD, $PN);
        $t->putBlock        ($offset, $LK, $LD, $LN);
       },
      Else                                                                      # Split the root node
       {my $p = $t->allocBlock       ($PK, $PD, $PN);                           # Create a new parent block
        $t->splitRoot   ($offset, $r, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);
        $t->upIntoData      ($p,      $LD);                                     # Left  points up to new parent
        $t->upIntoData      ($p,      $RD);                                     # Right points up to new parent
        $t->putBlock        ($p,      $PK, $PD, $PN);

        $t->putBlock        ($offset, $LK, $LD, $LN);
        $t->putBlock        ($r,      $RK, $RD, $RN);

        $t->firstFromMemory ($F);                                               # Update new root of tree
        $t->rootIntoFirst   ($F, $p);
        $t->firstIntoMemory ($F);
       };

      $t->leafFromNodes($RN);                                                   # Whether the right block is a leaf
      $t->leafFromNodes($RN, set=>rsi);                                         # NB: in this mode returns 0 if a leaf which is the opposite of what happens if we do not use a transfer register
      Cmp rsi, 0;

      IfGt                                                                      # Not a leaf
      Then
       {$t->area->address->setReg(rdi);                                         # Area address
        $r->setReg(rdx);                                                        # Parent offset
        Mov rcx, 0;                                                             # Start the loop at zero
        my $W = RegisterSize zmm1;

        Vmovdqu64 "[rsp-$W]", zmm $RN;                                          # Stack list of nodes to be re-parented
        For                                                                     # Reparent the children of the right hand side now known not to be a leaf
         {Mov esi, "[rsp+rcx*4-$W]";                                            # Offset of node
          Mov esi, "[rdi+rsi+$$t{loop}]";                                       # Offset of data node
          Mov "[rdi+rsi+$$t{up}]", edx;                                         # Update parent offset
         } rcx, $t->lengthRight + 1;
       };

      $t->putBlock        ($r,      $RK, $RD, $RN);                             # Save right block
     };                                                                         # Insert completed successfully
   }  structures => {tree => $tree},
      parameters => [qw(offset split)],
      name       => qq(Nasm::X86::Tree::splitNode-$$tree{length});

  $s->inline
   (structures => {tree   => $tree},
    parameters => {offset => $offset, split => my $p = V split => 1});

  $p                                                                            # Return a variable containing one if the node was split else zero.
 } # splitNode

sub Nasm::X86::Tree::splitNotRoot($$$$$$$$$$$)                                  #P Split a non root left node pushing its excess right and up.
 {my ($tree, $newRight, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN) = @_;      # Tree definition, variable offset in area of right node block, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm, right keys, data, node zmm
  @_ == 11 or confess "Eleven parameters required";

  my $w         = $tree->width;                                                 # Size of keys, data, nodes
  my $zw        = $tree->zWidthD;                                               # Number of dwords in a zmm
  my $zwn       = $tree->maxNodesZ;                                             # Maximum number of dwords that could be used for nodes in a zmm register.
  my $zwk       = $tree->maxKeysZ;                                              # Maximum number of dwords used for keys/data in a zmm
  my $lw        = $tree->maxKeys;                                               # Maximum number of keys in a node
  my $ll        = $tree->lengthLeft;                                            # Minimum node width on left
  my $lm        = $tree->lengthMiddle;                                          # Position of splitting key
  my $lr        = $tree->lengthRight;                                           # Minimum node on right
  my $lb        = $tree->lengthOffset;                                          # Position of length byte
  my $tb        = $tree->treeBits;                                              # Position of tree bits
  my $up        = $tree->up;                                                    # Position of up word in data
  my $transfer  = r8;                                                           # Transfer register
  my $transferD = r8d;                                                          # Transfer register as a dword
  my $transferW = r8w;                                                          # Transfer register as a  word
  my $work      = r9;                                                           # Work register as a dword

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    PushR $transfer, $work, 1..7;

    my $SK = dFromZ $LK, $ll * $w;                                              # Splitting key
    my $SD = dFromZ $LD, $ll * $w;                                              # Data corresponding to splitting key

    my $mask = sub                                                              # Set k7 to a specified bit mask
     {my ($prefix, @onesAndZeroes) = @_;                                        # Prefix bits, alternating zeroes and ones
      LoadBitsIntoMaskRegister(7, $prefix, @onesAndZeroes);                     # Load k7 with mask
     };

    &$mask("00", $zwk);                                                         # Copy Left node to right node

    Vmovdqu32    zmmM($RK, 7),  zmm($LK);                                       # Copy keys from left to right
    Vmovdqu32    zmmM($RD, 7),  zmm($LD);                                       # Copy data from left to right

    &$mask("0",  $zwn);
    Vmovdqu32    zmmM($RN, 7),  zmm($LN);                                       # Copy nodes from left to right

    &$mask("00", $lw-$zwk,  $lr, -$ll-1);                                       # Compress right data/keys
    Vpcompressd  zmmM($RK, 7),  zmm($RK);                                       # Compress copied right keys
    Vpcompressd  zmmM($RD, 7),  zmm($RD);                                       # Compress right copied data

    &$mask("0",  $lw-$zwk, $lr+1, -$lr-1);                                      # Compress right nodes
    Vpcompressd  zmmM($RN, 7),  zmm($RN);

    &$mask("11", $ll-$zwk, $ll);                                                # Clear left keys and data
    Vmovdqu32    zmmMZ($LK, 7), zmm($LK);
    Vmovdqu32    zmmMZ($LD, 7), zmm($LD);

    &$mask("1",  $ll-$zwk, $ll+1);                                              # Clear left nodes
    Vmovdqu32    zmmMZ($LN, 7), zmm($LN);

    &$mask("11", 2+$lr-$zw,  $lr);                                              # Clear right keys and data
    Vmovdqu32    zmmMZ($RK, 7), zmm($RK);
    Vmovdqu32    zmmMZ($RD, 7), zmm($RD);

    &$mask("1",  $lr-$zwk, $lr+1);                                              # Clear right nodes
    Vmovdqu32    zmmMZ($RN, 7), zmm($RN);

    my $t = $$s{tree};                                                          # Address tree

    &$mask("00", $zwk);                                                         # Area to clear in keys and data preserving last qword
    my $in = $t->insertionPoint($SK, $PK);                                      # The position at which the key would be inserted if this were a leaf
    $in->setReg($transfer);
    Kmovq k6, $transfer;                                                        # Mask shows insertion point
    Kandnq k5, k6, k7;                                                          # Mask shows expansion needed to make the insertion possible

    Vpexpandd zmmM($PK, 5), zmm($PK);                                           # Make room in parent keys and place the splitting key
    Vpexpandd zmmM($PD, 5), zmm($PD);                                           # Make room in parent data and place the data associated with the splitting key

    $SK->setReg($transfer);                                                     # Key to be inserted
    Vpbroadcastd zmmM($PK, 6), $transferD;                                      # Insert key

    $SD->setReg($transfer);                                                     # Data to be inserted
    Vpbroadcastd zmmM($PD, 6), $transferD;                                      # Insert data


    $in->setReg($transfer);                                                     # Next node up as we always expand to the right
    Shl $transfer, 1;
    Kmovq k4, $transfer;                                                        # Mask shows insertion point
    &$mask("0", $zwn);                                                          # Area to clear in keys and data preserving last qword
    Kandnq k3, k4, k7;                                                          # Mask shows expansion needed to make the insertion possible
    Vpexpandd zmmM($PN, 3), zmm($PN);                                           # Expand nodes

    $$p{newRight}->setReg($transfer);                                           # New right node to be inserted
    Vpbroadcastd zmmM($PN, 4), $transferD;                                      # Insert node

                                                                                # Lengths
    wRegFromZmm $work, $PK, $lb;                                                # Increment length of parent field
    Inc $work;
    wRegIntoZmm $work, $PK, $lb;

    Mov $work, $ll;                                                             # Lengths
    wRegIntoZmm $work, $LK, $lb;                                                # Left after split
    Mov $work, $lr;                                                             # Lengths
    wRegIntoZmm $work, $RK, $lb;                                                # Right after split

    &$mask("01", -$zwk);                                                        # Copy parent offset from left to right so that the new right node  still has the same parent
    Vmovdqu32 zmmM($RD, 7), zmm($LD);

    wRegFromZmm $transfer, $LK, $tb;
    Mov $work, $transfer;
    And $work, (1 << $ll) - 1;
    wRegIntoZmm $work, $LK, $tb;                                                # Left after split

    Mov $work, $transfer;
    Shr $work, $lm;
    And $work, (1 << $lr) - 1;
    wRegIntoZmm $work, $RK, $tb;                                                # Right after split

    Mov $work, $transfer;                                                       # Insert splitting key tree bit into parent at the location indicated by k5
    Shr $work, $ll;
    And  $work, 1;                                                              # Tree bit to be inserted parent at the position indicated by a single 1 in k5 in parent
    wRegFromZmm $transfer, $PK, $tb;                                            # Tree bits from parent

    Cmp  $work, 0;                                                              # Are we inserting a zero into the tree bits?
    IfEq
    Then                                                                        # Inserting zero
     {InsertZeroIntoRegisterAtPoint k6, $transfer;                              # Insert a zero into transfer at the point indicated by k5
     },
    Else                                                                        # Inserting one
     {InsertOneIntoRegisterAtPoint k6, $transfer;                               # Insert a zero into transfer at the point indicated by k5
     };
    wRegIntoZmm $transfer, $PK, $tb;                                            # Save parent tree bits after split

    PopR;
   }
  structures => {tree => $tree},
  parameters => [qw(newRight)],
  name       => join('-', qq(Nasm::X86::Tree::splitNotRoot), $$tree{length},
                          $lw, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);

  $s->inline(
    structures => {tree => $tree},
    parameters => {newRight => $newRight});
 } # splitNotRoot

sub Nasm::X86::Tree::splitRoot($$$$$$$$$$$$)                                    #P Split a non root node into left and right nodes with the left half left in the left node and splitting key/data pushed into the parent node with the remainder pushed into the new right node.
 {my ($tree, $nLeft, $nRight, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN) = @_;# Tree definition, variable offset in area of new left node block, variable offset in area of new right node block, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm, right keys, data , nodes zmm
  @_ == 12 or confess "Twelve parameters required";

  my $w         = $tree->width;                                                 # Size of keys, data, nodes
  my $zw        = $tree->zWidthD;                                               # Number of dwords in a zmm
  my $zwn       = $tree->maxNodesZ;                                             # Maximum number of dwords that could be used for nodes in a zmm register.
  my $zwk       = $tree->maxKeysZ;                                              # Maximum number of dwords used for keys/data in a zmm
  my $lw        = $tree->maxKeys;                                               # Maximum number of keys in a node
  my $ll        = $tree->lengthLeft;                                            # Minimum node width on left
  my $lm        = $tree->lengthMiddle;                                          # Position of splitting key
  my $lr        = $tree->lengthRight;                                           # Minimum node on right
  my $lb        = $tree->lengthOffset;                                          # Position of length byte
  my $tb        = $tree->treeBits;                                              # Position of tree bits
  my $transfer  = r8;                                                           # Transfer register
  my $transferD = r8d;                                                          # Transfer register as a dword
  my $transferW = r8w;                                                          # Transfer register as a  word

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description

    my $mask = sub                                                              # Set k7 to a specified bit mask
     {my ($prefix, @onesAndZeroes) = @_;                                        # Prefix bits, alternating zeroes and ones
      LoadBitsIntoMaskRegister(7, $prefix, @onesAndZeroes);                     # Load k7 with mask
     };

    my $t = $$s{tree};                                                          # Address tree

    PushR $transfer, 6, 7;

    $t->maskForFullKeyArea(7);                                                  # Mask for keys area
    $t->maskForFullNodesArea(6);                                                # Mask for nodes area

    Mov $transfer, -1;
    Vpbroadcastd zmmM($PK, 7), $transferD;                                      # Force keys to be high so that insertion occurs before all of them

    Mov $transfer, 0;
    Vpbroadcastd zmmM($PD, 7), $transferD;                                      # Zero other keys and data
    Vpbroadcastd zmmM($RK, 7), $transferD;
    Vpbroadcastd zmmM($RD, 7), $transferD;

    Mov $transfer, 0;
    Vpbroadcastd zmmM($PN, 6), $transferD;
    Vpbroadcastd zmmM($RN, 6), $transferD;

    my $newRight = $$p{newRight};
    $t->splitNotRoot($newRight, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);   # Split the root node as if it were a non root node

    $$p{newLeft} ->dIntoZ($PN, 0);                                              # Place first - left sub node into new root
    $$p{newRight}->dIntoZ($PN, 4);                                              # Place second - right sub node into new root

    Kshiftrw k7, k7, 1;                                                         # Reset parent keys/data outside of single key/data
    Kshiftlw k7, k7, 1;
    Mov $transfer, 0;
    Vpbroadcastd zmmM($PK, 7), $transferD;

    Mov $transfer, 1;                                                           # Lengths
    wRegIntoZmm $transfer, $PK, $lb;                                            # Left after split

    wRegFromZmm $transfer, $PK, $tb;                                            # Parent tree bits
    And $transfer, 1;
    wRegIntoZmm $transfer, $PK, $tb;

    PopR;
   }
  structures => {tree => $tree},
  parameters => [qw(newLeft newRight)],
  name       => join '-', "Nasm::X86::Tree::splitRoot", $$tree{length},
                  $lw, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN;

  $s->inline
   (structures => {tree => $tree},
    parameters => {newLeft => $nLeft, newRight => $nRight});
 } # splitRoot

sub Nasm::X86::Tree::put($$$)                                                   # Put a variable key and data into a tree. The data could be a tree descriptor to place a sub tree into a tree at the indicated key.
 {my ($tree, $key, $data) = @_;                                                 # Tree definition, variable key containing a number for a normal key or the offset in the area of a zmm block containing the key, data as a variable or a tree descriptor
  @_ == 3 or confess "Three parameters";

  my $dt = ref($data) =~ m(Tree);                                               # We are inserting a sub tree if true

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    Block
     {my ($success) = @_;                                                       # End label
      PushR my $Q = r13, my $setEq = r14, my $setLt = r15,                      # Offset of current node, insertion point, equality point
            my ($F, $K, $D, $N, $key) = zmm reverse 27..31;                     # First, keys, data, nodes, search key
      my $t = $$s{tree};
      my $k = $$p{key};
      my $d = $$p{data};
      my $S = $$p{subTree};
      my $a = $t->area;

      $k->setReg(rdi);                                                          # Load key once
      if ($t->stringTree)                                                       # Key is the offset to a key in the same area as the tree
       {$a->address->setReg(rsi);
        Vmovdqu64 zmm($key), "[rsi+rdi]";
       }
      else                                                                      # Key is any double word
       {$k->setReg(rdi);
        Vpbroadcastd zmm($key), edi;
       }

      my $start = SetLabel;                                                     # Start the descent through the tree

      $t->firstFromMemory($F);
      $t->rootFromFirst($F, set=>$Q);                                           # Start the descent at the root node

      Cmp $Q, 0;
      IfEq
      Then                                                                      # First entry as there is no root node
       {my $block = $t->allocBlock($K, $D, $N);
        $k->dIntoZ                ($K, 0);
        $d->dIntoZ                ($D, 0);
        $t->incLengthInKeys       ($K);
        $t->setOrClearTreeBitToMatchContent($K, K(key => 1), $S);
        $t->putBlock($block,       $K, $D, $N);
        $t->rootIntoFirst         ($F, $block);
        $t->incSizeInFirst        ($F);
        $t->firstIntoMemory       ($F);                                         # First back into memory
        Jmp $success;
       };

      my $descend = SetLabel;                                                   # Descend to the next level

      $t->getBlock($Q, $K, $D, $N);                                             # Get the current block from memory

      $t->indexEqLt($key, $K, $setEq, $setLt);                                  # Check for an equal key
      IfNz                                                                      # Equal key found
      Then                                                                      # Overwrite the existing key/data
       {$t->overWriteKeyDataTreeInLeaf($setEq, $K, $D, $k, $d, $S);
        $t->putBlock                  ($Q,     $K, $D, $N);
        Jmp $success;
       };

      $t->lengthFromKeys($K, set=>rsi);
      Cmp rsi, $t->maxKeys;
      IfGe
      Then                                                                      # Split full blocks
       {$t->splitNode(V offset => $Q);                                          # Split node is a large function that is hopefully called infrequently so crating a register parameter is, perhaps, not worth the effort
        Jmp $start;                                                             # Restart the descent now that this block has been split
       };

      $t->leafFromNodes($N, set=>rsi);                                          # NB: in this mode returns 0 if a leaf which is the opposite of what happens if we do not use a transfer register
      Cmp rsi, 0;
      IfEq
      Then                                                                      # On a leaf
       {$t->insertKeyDataTreeIntoLeaf($setLt, $F, $K, $D, $k, $d, $S);
        $t->putBlock                 ($Q,         $K, $D, $N);
        $t->firstIntoMemory          ($F);                                      # First back into memory
        Jmp $success;
       };

      dFromPointInZ($setLt, $N, set=>$Q);                                       # The node to the left of the insertion point - this works because the insertion point can be up to one more than the maximum number of keys
      Jmp $descend;                                                             # Descend to the next level
     };
    PopR;
   } name => qq(Nasm::X86::Tree::put-$$tree{length}-$$tree{stringTree}),
     structures => {tree=>$tree},
     parameters => [qw(key data subTree)];


  if ($dt)                                                                      # Put a sub tree
   {$s->call(structures => {tree    => $tree},
             parameters => {key     => (ref($key) ? $key : K key => $key),
                            data    => $data->first,
                            subTree => K(subTree => 1)});
   }
  else                                                                          # Not a sub tree
   {$s->call(structures => {tree    => $tree},
             parameters => {key     => (ref($key)  ? $key  : K key  => $key),
                            data    => (ref($data) ? $data : K data => $data),
                            subTree => K(subTree => 0)});
   }
 } # put

#D2 Find                                                                        # Find a key in the tree. Trees have dword integer keys and so can act as arrays as well.

sub Nasm::X86::Tree::zero($)                                                    #P Zero the return fields of a tree descriptor.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";
  $tree->found  ->copy(0);                                                      # Key not found
  $tree->data   ->copy(0);                                                      # Data not yet found
  $tree->subTree->copy(0);                                                      # Not yet a sub tree
  $tree->offset ->copy(0);                                                      # Offset not known
  $tree                                                                         # Chaining
 }

sub Nasm::X86::Tree::find($$)                                                   # Find a key in a tree and tests whether the found data is a sub tree.  The results are held in the variables "found", "data", "subTree" addressed by the tree descriptor. The key just searched for is held in the key field of the tree descriptor. The point at which it was found is held in B<found> which will be zero if the key was not found.
 {my ($tree, $key) = @_;                                                        # Tree descriptor, key field to search for which can either be a variable containing a double word for a normal tree or a zmm register containing the key to be sought for a string tree.
  @_ == 2 or confess "Two parameters";

  if ($tree->stringTree)                                                        # Key in variable
   {confess "Zmm required"                if ref($key);
    confess "Bound zmm required not $key" if $key =~ m(\A(zmm)?(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15)\Z);
   }

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    my $F = zmm1, my $K = zmm2, my $D = zmm3, my $N = zmm4, my $zKey = zmm5;    # We are boldly assuming that these registers are not being used independently
    PushR my $Q = r15, my $loop = r14, my $equals = r13, my $insert = r12;

    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      my $t = $$s{tree};                                                        # Tree to search
      $t->zero;                                                                 # Clear search fields

      if (ref($$p{key}))                                                        # Does not apply to string trees which uses zmm registers to pass in the keys.
       {$t->key->copy(my $k = $$p{key});                                        # Copy in key so we know what was searched for
       }

      $t->firstFromMemory      ($F);                                            # Load first block

      $t->rootFromFirst($F, set => $Q);                                         # Start the search from the root

      Cmp $Q, 0;
      Je $success;                                                              # Empty tree so we have not found the key

      if ($tree->stringTree)                                                    # Load key just once
       {$zKey = $key;
       }
      else
       {$$p{key}->setReg(rdi);
        Vpbroadcastd zmm($zKey), edi;
       }

      uptoNTimes                                                                # Step down through tree
       {my (undef, $start) = @_;
        $t->getBlock($Q, $K, $D, $N);                                           # Get the keys/data/nodes

        $t->indexEqLt($zKey, $K, $equals, $insert);                             # The position of a key in a zmm equal to the specified key as a point in a variable.
        IfNz                                                                    # Result mask is non zero so we must have found the key
        Then
         {dFromPointInZ $equals, $D, set=>rsi;                                  # Get the corresponding data
          $t->data  ->copy(rsi);                                                # Data associated with the key
          $t->found ->copy($equals);                                            # Show found
          $t->offset->copy($Q);                                                 # Offset of the containing block
          $t->getTreeBit($K, $equals, set => rdx);                              # Get corresponding tree bit
          $t->subTree->copy(rdx);                                               # Save corresponding tree bit
          Jmp $success;                                                         # Return
         };

        $t->leafFromNodes($N, set=>rsi),                                        # Check whether this is a leaf by looking at the first sub node - if it is zero this must be a leaf as no node van have a zero offset in an area
        Cmp rsi, 0;                                                             # Leaf if zero
        Jz $success;                                                            # Return

        dFromPointInZ     ($insert,  $N, set =>  $Q);                           # Get the corresponding offset to the next sub tree
        Sub $loop, 1;
        Jnz $start;                                                             # Keep going but not for ever
       } $loop, 99;                                                             # Loop a limited number of times
      PrintErrTraceBack "Stuck in find";                                        # We seem to be looping endlessly
     };                                                                         # Find completed successfully

    PopR;
   } parameters => [qw(key)],
     structures => {tree=>$tree},
     name       => qq(Nasm::X86::Tree::find-$$tree{length}-$$tree{stringTree});

  $s->inline(structures => {tree => $tree},
             parameters => {key  => ref($key) || $key =~ m(\Azmm) ? $key : K key => $key});
 } # find

sub Nasm::X86::Tree::findFirst($)                                               # Find the first element in a tree and set B<found>|B<key>|B<data>|B<subTree> to show the result.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Successfully completed

      my $t = $$s{tree};                                                        # Tree to search
         $t->zero;                                                              # Key not found

      PushR my $F = 31, my $K = 30, my $D = 29, my $N = 28;

      $t->firstFromMemory($F);                                                  # Update the size of the tree
      my $size = $t->sizeFromFirst($F);                                         # Size of tree

      If $size == 0,                                                            # Empty tree
      Then
       {$t->found->copy(0);                                                     # Could not find anything
        Jmp $success
       };

      my $root = $t->rootFromFirst($F);                                         # Root of tree
      $t->getBlock($root, $K, $D, $N);                                          # Load root

      K(loop => 99)->for(sub                                                    # Step down through the tree a reasonable number of times
       {my ($i, $start, $next, $end) = @_;

        If $t->leafFromNodes($N) > 0,                                           # Leaf node means we have arrived
        Then
         {my $k = dFromZ($K, 0);
          my $d = dFromZ($D, 0);
          my $b = $t->getTreeBit($K, K key => 1);
          $t->found  ->copy(1);
          $t->key    ->copy($k);
          $t->data   ->copy($d);
          $t->subTree->copy($b);
          Jmp $success
         };

        my $n = dFromZ($N, 0);
        $t->getBlock($n, $K, $D, $N);
       });
      PrintErrTraceBack "Stuck looking for first";
     };                                                                         # Find completed successfully
    PopR;
   } structures=>{tree=>$tree},
     name => qq(Nasm::X86::Tree::findFirst-$$tree{length}-$$tree{stringTree});

  $s->call(structures=>{tree => $tree});
 } # findFirst

sub Nasm::X86::Tree::findLast($)                                                # Find the last key in a tree - crucial for stack like operations.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Successfully completed

      my $t = $$s{tree}->zero;                                                  # Tree to search

      PushR my $F = 31, my $K = 30, my $D = 29, my $N = 28;

      $t->firstFromMemory($F);                                                  # Update the size of the tree
      my $size = $t->sizeFromFirst($F);                                         # Number of entries in tree

      If $size > 0,                                                             # Non empty tree
      Then
       {my $root = $t->rootFromFirst($F);                                       # Root of tree

        $t->getBlock($root, $K, $D, $N);                                        # Load root

        K(loop => 99)->for(sub                                                  # Step down through the tree a reasonable number of times
         {my ($i, $start, $next, $end) = @_;
          my $l = $t->lengthFromKeys($K);

          If $t->leafFromNodes($N) > 0,                                         # Leaf node means we have arrived
          Then
           {my $o  = ($l - 1) * $t->width;
            my $k = dFromZ($K, $o);
            my $d = dFromZ($D, $o);
            my $b = $t->getTreeBit($K, $l);

            $t->found  ->copy(1);
            $t->key    ->copy($k);
            $t->data   ->copy($d);
            $t->subTree->copy($b);
            Jmp $success
           };

          my $O = $l * $t->width;
          my $n = dFromZ($N, $O);                                               # Step down to the next level
          $t->getBlock($n, $K, $D, $N);
         });
        PrintErrTraceBack "Stuck looking for last";
       },
      Else
       {$t->found->copy(0);
       };
     };                                                                         # Find completed successfully
    PopR;
   } structures=>{tree=>$tree},
     name => qq(Nasm::X86::Tree::findLast-$$tree{length}-$$tree{stringTree});

  $s->call(structures=>{tree => $tree});                                        # Inline causes very long assembly times so we call instead.
 } # findLast

sub Nasm::X86::Tree::findNext($$)                                               # Find the next key greater than the one specified.
 {my ($tree, $key) = @_;                                                        # Tree descriptor, key
  @_ == 2 or confess "Two parameters";
  ref($key) =~ m(Variable) or confess "Variable required";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      PushR my $F = 31, my $K = 30, my $D = 29, my $N = 28;
      my $t = $$s{tree}->zero;                                                  # Tree to search
      my $k = $$p{key};                                                         # Key to find
      $t->key->copy($k);                                                        # Copy in key so we know what was searched for

      $t->firstFromMemory      ($F);                                            # Load first block
      my $Q = $t->rootFromFirst($F);                                            # Start the search from the root
      If $Q == 0,
      Then                                                                      # Empty tree so we have successfully not found the key
       {Jmp $success;                                                           # Return
       };

      my $li = V(key => 0);                                                     # Offset of last not right tells us where to continue the search -
      my $lQ = V(key => 0);                                                     # Insertion point of last non right

      K(loop=>99)->for(sub                                                      # Step down through tree
       {my ($index, $start, $next, $end) = @_;

        $t->getBlock($Q, $K, $D, $N);                                           # Get the keys/data/nodes
        my $lp   = K(key => 1) << $t->lengthFromKeys($K);                       # Point to last node in nodes area
        my $i = $t->insertionPoint($k, $K);                                     # The insertion point
        If $t->leafFromNodes($N) > 0,
        Then                                                                    # On a leaf
         {If $i == $lp,
          Then                                                                  # Last in leaf so reposition on last not right
           {If $li == 0, Then {Jmp $success};                                   # Greater than all keys
            $t->getBlock($li, $K, $D, $N);
            $i->copy($lQ);
           };
          $t->found  ->copy($i);                                                # Key found at this point
          $t->key    ->copy($i->dFromPointInZ($K));                             # Save key
          $t->data   ->copy($i->dFromPointInZ($D));                             # Save data
          $t->subTree->copy($t->getTreeBit   ($K, $i));                         # Save sub tree
          $t->offset ->copy($Q);                                                # Save offset
          Jmp $success;                                                         # Return
         };

        my $n = $i->dFromPointInZ($N);                                          # Get the corresponding data
        If $i != $lp,
        Then                                                                    # Not descending through the last right
         {$li->copy($Q);
          $lQ->copy($i);
         };
        $Q->copy($n);                                                           # Corresponding node
       });
      PrintErrTraceBack "Stuck in find next";                                   # We seem to be looping endlessly
     };                                                                         # Find completed successfully
    PopR;
   } parameters => [qw(key)],
     structures => {tree=>$tree},
     name       => qq(Nasm::X86::Tree::findNext-$$tree{length}-$$tree{stringTree});

  $s->call(structures=>{tree => $tree}, parameters=>{key => $key});
 } # findNext

sub Nasm::X86::Tree::findPrev($$)                                               # Find the previous key less than the one specified.
 {my ($tree, $key) = @_;                                                        # Tree descriptor, key
  @_ == 2 or confess "Two parameters";
  ref($key) =~ m(Variable) or confess "Variable required";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      PushR my $F = 31, my $K = 30, my $D = 29, my $N = 28;
      my $t = $$s{tree}->zero;                                                  # Tree to search
      my $k = $$p{key};                                                         # Key to find
      $t->key->copy($k);                                                        # Copy in key so we know what was searched for

      $t->firstFromMemory      ($F);                                            # Load first block
      my $Q = $t->rootFromFirst($F);                                            # Start the search from the root
      If $Q == 0, Then {Jmp $success};                                          # Empty tree so we have successfully not found the key

      my $li = V key => 0;                                                      # Offset of last not right tells us where to continue the search -
      my $lQ = V key => 0;                                                      # Insertion point of last non right

      K(loop => 99)->for(sub                                                    # Step down through tree
       {my ($index, $start, $next, $end) = @_;
        $t->getBlock($Q, $K, $D, $N);                                           # Get the keys/data/nodes
        my $i = $t->insertionPoint($k, $K);                                     # The insertion point
        If $i > 1,
        Then
         {my $j = $i >> K key => 1;
          If $j->dFromPointInZ($K) == $k,
          Then
           {$i->copy($j);
           };
         };

        If $t->leafFromNodes($N) > 0,
        Then                                                                    # On a leaf
         {If $i == 1,
          Then                                                                  # First in leaf so reposition on last not left
           {If $li == 0, Then {Jmp $success};                                   # Greater than all keys
            $t->getBlock($li, $K, $D, $N);
            $i->copy($lQ);
           };
          $i->copy($i >> K(one => 1));
          $t->found  ->copy($i);                                                # Key found at this point
          $t->key    ->copy($i->dFromPointInZ($K));                             # Save key
          $t->data   ->copy($i->dFromPointInZ($D));                             # Save data
          $t->subTree->copy($t->getTreeBit   ($K, $i));                         # Save sub tree
          $t->offset ->copy($Q);                                                # Save offset
          Jmp $success;                                                         # Return
         };

        my $n = $i->dFromPointInZ($N);                                          # Get the corresponding data
        If $i != 1,
        Then                                                                    # Not descending through the first left
         {$li->copy($Q);
          $lQ->copy($i);
         };
        $Q->copy($n);                                                           # Corresponding node
       });
      PrintErrTraceBack "Stuck in find prev";                                   # We seem to be looping endlessly
     };                                                                         # Find completed successfully
    PopR;
   } parameters => [qw(key)],
     structures => {tree=>$tree},
     name       => qq(Nasm::X86::Tree::findPrev-$$tree{length}-$$tree{stringTree});

  $s->call(structures=>{tree => $tree}, parameters=>{key => $key});
 } # findPrev

sub Nasm::X86::Tree::findAndReload($$)                                          #P Find a key in the specified tree and clone it is it is a sub tree.
 {my ($t, $key) = @_;                                                           # Tree descriptor, key as a dword
  @_ == 2 or confess "Two parameters";

  $t->find($key);                                                               # Find the key
  If $t->found > 0,                                                             # Make the found data the new  tree
  Then
   {$t->first->copy($t->data);                                                  # Copy the data variable to the first variable without checking whether it is valid
   };
 }

sub Nasm::X86::Tree::findSubTree($$)                                            # Find a key in the specified tree and create a sub tree from the data field if possible.
 {my ($tree, $key) = @_;                                                        # Tree descriptor, key as a dword
  @_ == 2 or confess "Two parameters";

  my $t = $tree->DescribeTree;                                                  # The sub tree we are attempting to load
     $t->copyDescriptor($tree);                                                 # Position on the tree

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    my $t = $$s{tree};
    $t->find($$p{key});                                                         # Find the key

    ifAnd [sub{$t->found > 0}, sub{$t->subTree > 0}],                           # Make the found data the new  tree
    Then
     {$t->first->copy($t->data);                                                # Copy the data variable to the first variable without checking whether it is valid
      $t->found->copy(1);
     },
    Else
     {$t->first->copy(-1);                                                      # Remove any hint of a tree
      $t->found->copy(0);                                                       # We did not find the sub tree
     };
    } parameters => [qw(key)],
      structures => {tree=>$t},
      name       => qq(Nasm::X86::Tree::findSubTree);

  $s->call(structures => {tree => $t},
           parameters => {key => ref($key) ? $key : K key => $key});

  $t
 }

sub Nasm::X86::Tree::leftOrRightMost($$$$)                                      #P Return the offset of the left most or right most node.
 {my ($tree, $dir, $node, $offset) = @_;                                        # Tree descriptor, direction: left = 0 or right = 1, start node,  offset of located node
  @_ == 4 or confess "Four parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      my $t        = $$s{tree};                                                 # Tree
         $t->first->copy(my $F = $$p{node});                                    # First block
      my $area = $t->area;                                                      # Area
      PushR rax, 8, 9, 29..31;

      K(loopLimit=>9)->for(sub                                                  # Loop a reasonable number of times
       {my ($index, $start, $next, $end) = @_;
        $t->getBlock($F, 31, 30, 29);                                           # Get the first keys block
        my $n = dFromZ 29, 0;                                                   # Get the node block offset from the data block loop
        If $n == 0,
        Then                                                                    # Reached the end so return the containing block
         {$$p{offset}->copy($F);
          Jmp $success;
         };
        if ($dir == 0)                                                          # Left most
         {my $l = dFromZ 29, 0;                                                 # Get the left most node
          $F->copy($l);                                                         # Continue with the next level
         }
        else                                                                    # Right most
         {my $l = $t->lengthFromKeys(31);                                       # Length of the node
          my $r = dFromZ 31, $l;                                                # Get the right most child
          $F->copy($r);                                                         # Continue with the next level
         }
       });
      PrintErrStringNL "Stuck in LeftOrRightMost";
      Exit(1);
     };                                                                         # Insert completed successfully
    PopR;
   } structures => {tree => $tree},
     parameters => [qw(node offset)],
     name       => $dir==0 ? qq(Nasm::X86::Tree::leftMost-$$tree{length}-$$tree{smallTree}-$$tree{lowTree}-$$tree{stringTree}) :
                             qq(Nasm::X86::Tree::rightMost-$$tree{length}-$$tree{smallTree}-$$tree{lowTree}-$$tree{stringTree});

  $s->call
   (structures => {tree=>$tree},
    parameters => {node => $node, offset=>$offset});
 }

sub Nasm::X86::Tree::leftMost($$$)                                              #P Return the offset of the left most node from the specified node.
 {my ($t, $node, $offset) = @_;                                                 # Tree descriptor, start node, returned offset
  @_ == 3 or confess "Three parameters";
  $t->leftOrRightMost(0, $node, $offset)                                        # Return the left most node
 }

sub Nasm::X86::Tree::rightMost($$$)                                             #P Return the offset of the left most node from the specified node.
 {my ($t, $node, $offset) = @_;                                                 # Tree descriptor, start node, returned offset
  @_ == 3 or confess "Three parameters";
  $t->leftOrRightMost(1, $node, $offset)                                        # Return the right most node
 }

sub Nasm::X86::Tree::depth($$)                                                  #P Return the depth of a node within a tree.
 {my ($tree, $node) = @_;                                                       # Tree descriptor, node
  @_ == 2 or confess "Two parameters required";
  PrintErrTraceBack "Rewrite me";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      my $t = $$s{tree};                                                        # Tree
      my $area = $tree->area;                                                   # Area
      my $N = $$p{node};                                                        # Starting node

      PushR 8, 9, 14, 15, 30, 31;
      my $tree = $N->clone('tree');                                             # Start at the specified node

      K(loop => 9)->for(sub                                                     # Step up through tree
       {my ($index, $start, $next, $end) = @_;
        $t->getKeysData($tree, 31, 30, r8, r9);                                 # Get the first node of the tree
        my $P = $t->getUpFromData(30);                                          # Parent
        If $P == 0,
        Then                                                                    # Empty tree so we have not found the key
         {$$p{depth}->copy($index+1);                                           # Key not found
          Jmp $success;                                                         # Return
         };
        $tree->copy($P);                                                        # Up to next level
       });
      PrintErrStringNL "Stuck in depth";                                        # We seem to be looping endlessly
      Exit(1);
     };                                                                         # Insert completed successfully
    PopR;
   }  structures => {tree => $tree},
      parameters => [qw(node depth)],
      name       => qq(Nasm::X86::Tree::depth-$$tree{length}-$$tree{smallTree}-$$tree{lowTree}-$$tree{stringTree});

  $s->call(structures => {tree => $tree->copyDescriptor},
           parameters => {node => $node, depth => my $d = V depth => 0});

  $d
 } # depth

#D2 Sub trees                                                                   # Construct trees of trees - all private.

sub Nasm::X86::Tree::isTree($$$)                                                #P Set the Zero Flag to oppose the tree bit in the numbered zmm register holding the keys of a node to indicate whether the data element indicated by the specified register is an offset to a sub tree in the containing area or not.
{my ($t, $zmm, $point) = @_;                                                    # Tree descriptor, numbered zmm register holding the keys for a node in the tree, register showing point to test
 @_ == 3 or confess "Three parameters";

  my $z = registerNameFromNumber $zmm;                                          # Full name of zmm register
  my $o = $t->treeBits;                                                         # Bytes from tree bits to end of zmm
  my $w = $t->zWidth;                                                           # Size of zmm register
  Vmovdqu64    "[rsp-$w]", $z;                                                  # Write beyond stack
  Test $point, "[rsp-$w+$o]";                                                   # Test the tree bit under point
 } # isTree

sub Nasm::X86::Tree::getTreeBit($$$%)                                           #P Get the tree bit from the numbered zmm at the specified point and return it in a variable as a one or a zero.
 {my ($t, $zmm, $point, %options) = @_;                                         # Tree descriptor, register showing point to test, numbered zmm register holding the keys for a node in the tree, options
  @_ >= 3 or confess "Three or more parameters";

  if (ref($point))                                                              # Point is a variable so we will do everything in variables
   {$t->getTreeBits($zmm, rdi);                                                 # Tree bits
    $point->setReg(rsi);
    And rdi, rsi;                                                               # Write beyond stack
    my $r = V treeBit => 0;
    Cmp di, 0;
    IfNe Then {$r->copy(1)};
    return $r
   }
  else                                                                          # Point is a register so we will do everything in registers
   {my $s = $options{set} // rdi;                                               # The register we are going to be set to something other than zero if the tree bit is set
    confess "Target cannot be rsi" if $s eq rsi;
    $t->getTreeBits($zmm, $s);                                                  # Tree bits
    And $s, $point;                                                             # Jnz jumps if the tree bit has been set
   }
 }

sub Nasm::X86::Tree::setOrClearTreeBit($$$$)                                    #P Set or clear the tree bit selected by the specified point in the numbered zmm register holding the keys of a node to indicate that the data element indicated by the specified register is an offset to a sub tree in the containing area.
 {my ($t, $set, $point, $zmm) = @_;                                             # Tree descriptor, set if true else clear, register holding point to set, numbered zmm register holding the keys for a node in the tree
  @_ == 4 or confess "Four parameters";
  #CheckGeneralPurposeRegister($point);
  my $z = registerNameFromNumber $zmm;                                          # Full name of zmm register
  my $o = $t->treeBits;                                                         # Tree bits to end of zmm
  my $r = registerNameFromNumber $point;
  PushR $z;                                                                     # Push onto stack so we can modify it
  if ($set)                                                                     # Set the indexed bit
   {And $point, $t->treeBitsMask;                                               # Mask tree bits to prevent operations outside the permitted area
    Or "[rsp+$o]", $point;                                                      # Set tree bit in zmm
   }
  else                                                                          # Clear the indexed bit
   {And $point, $t->treeBitsMask;                                               # Mask tree bits to prevent operations outside the permitted area
    Not $point;
    And "[rsp+$o]", $point;
   }
  PopR;                                                                         # Retrieve zmm
 } # setOrClearTree

sub Nasm::X86::Tree::setTreeBit($$$)                                            #P Set the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.
 {my ($t, $zmm, $point) = @_;                                                   # Tree descriptor, numbered zmm register holding the keys for a node in the tree, register holding the point to clear
  @_ == 3 or confess "Three parameters";
  $t->setOrClearTreeBit(1, $point, $zmm);
 } # setTree

sub Nasm::X86::Tree::clearTreeBit($$$)                                          #P Clear the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.
{my ($t, $zmm, $point) = @_;                                                    # Tree descriptor, numbered zmm register holding the keys for a node in the tree, register holding register holding the point to set
  @_ == 3 or confess "Three parameters";
  $t->setOrClearTreeBit(0, $point, $zmm);
 } # clearTree


sub Nasm::X86::Tree::setOrClearTreeBitToMatchContent($$$$)                      #P Set or clear the tree bit pointed to by the specified register depending on the content of the specified variable.
 {my ($t, $zmm, $point, $content) = @_;                                         # Tree descriptor, numbered keys zmm, register indicating point, content indicating zero or one
  @_ == 4 or confess "Four parameters";

  if (ref($point))                                                              # Point is a variable so we must put it in a register
   {PushR 15;
    $point->setReg(15);
    If $content > 0,                                                            # Content represents a tree
    Then
     {$t->setTreeBit($zmm, r15);
     },
    Else                                                                        # Content represents a variable
     {$t->clearTreeBit($zmm, r15);
     };
    PopR;
   }
  Else
   {If $content > 0,                                                            # Content represents a tree
    Then
     {$t->setTreeBit($zmm, $point);
     },
    Else                                                                        # Content represents a variable
     {$t->clearTreeBit($zmm, $point);
     };
   }
 }

sub Nasm::X86::Tree::getTreeBits($$$)                                           #P Load the tree bits from the numbered zmm into the specified register.
 {my ($t, $zmm, $register) = @_;                                                # Tree descriptor, numbered zmm, target register
  @_ == 3 or confess "Three parameters";

  wRegFromZmm $register, $zmm, $t->treeBits;
  And $register, $t->treeBitsMask;
 }

sub Nasm::X86::Tree::setTreeBits($$$)                                           #P Put the tree bits in the specified register into the numbered zmm.
 {my ($t, $zmm, $register) = @_;                                                # Tree descriptor, numbered zmm, target register
  @_ == 3 or confess "Three parameters";
  And $register, $t->treeBitsMask;
  wRegIntoZmm $register, $zmm, $t->treeBits;
 }

sub Nasm::X86::Tree::insertTreeBit($$$$)                                        #P Insert a zero or one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.
 {my ($t, $onz, $zmm, $point) = @_;                                             # Tree descriptor, 0 - zero or 1 - one, numbered zmm, register indicating point
  @_ == 4 or confess "Four parameters";
  my $z = registerNameFromNumber $zmm;
  my $p = registerNameFromNumber $point;
  PushR my @save = my ($bits) = ChooseRegisters(1, $point);                     # Tree bits register
  $t->getTreeBits($zmm, $bits);                                                 # Get tree bits
  if ($onz)
   {InsertOneIntoRegisterAtPoint ($p, $bits);                                   # Insert a one into the tree bits at the indicated location
   }
  else
   {InsertZeroIntoRegisterAtPoint($p, $bits);                                   # Insert a zero into the tree bits at the indicated location
   }
  $t->setTreeBits($zmm, $bits);                                                 # Put tree bits
  PopR;
 }

sub Nasm::X86::Tree::insertZeroIntoTreeBits($$$)                                #P Insert a zero into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.
 {my ($t, $zmm, $point) = @_;                                                   # Tree descriptor, numbered zmm, register indicating point
  @_ == 3 or confess "3 parameters";
  $t->insertTreeBit(0, $zmm, $point);                                           # Insert a zero into the tree bits field in the numbered zmm at the specified point
 }

sub Nasm::X86::Tree::insertOneIntoTreeBits($$$)                                 #P Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.
 {my ($t, $zmm, $point) = @_;                                                   # Tree descriptor, numbered zmm, register indicating point
  @_ == 3 or confess "Three parameters";
  $t->insertTreeBit(1, $zmm, $point);                                           # Insert a one into the tree bits field in the numbered zmm at the specified point
 }

sub Nasm::X86::Tree::insertIntoTreeBits($$$$)                                   #P Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.
 {my ($t, $zmm, $point, $content) = @_;                                         # Tree descriptor, numbered zmm, register indicating point, bit to insert
  @_ == 4 or confess "Four parameters";

  if (ref($point))                                                              # Point is a variable so we must put into a register
   {PushR 15;
    $point->setReg(15);
    If $content > 0,                                                            # Content represents a one
    Then
     {$t->insertOneIntoTreeBits ($zmm, r15);
     },
    Else                                                                        # Content represents a zero
     {$t->insertZeroIntoTreeBits($zmm, r15);
     };
    PopR;
   }
  else
   {If $content > 0,                                                            # Content represents a one
    Then
     {$t->insertOneIntoTreeBits ($zmm, $point);
     },
    Else                                                                        # Content represents a zero
     {$t->insertZeroIntoTreeBits($zmm, $point);
     };
   }
 }

#D2 Delete                                                                      # Delete a key from the tree

sub Nasm::X86::Tree::extract($$$$$)                                             #P Extract the key/data/node and tree bit at the specified point from the block held in the specified zmm registers.
 {my ($tree, $point, $K, $D, $N) = @_;                                          # Tree descriptor, point at which to extract, keys zmm, data zmm, node zmm
  @_ == 5 or confess "Five parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $t = $$s{tree};                                                          # Tree to search
    if ($DebugMode)                                                             # With checking
     {If $t->leafFromNodes($N) == 0,                                            # If the zero Flag is zero then this is not a leaf
      Then                                                                      # We can only perform this operation on a leaf
       {PrintErrTraceBack "Cannot extract from a non leaf node";
       };
     }

    PushR 7, 15;

    my $q = $$p{point};                                                         # Point at which to extract
    $t->data->copy($q->dFromPointInZ($D));                                      # Data at point
    $t->subTree->copy($t->getTreeBit($K, $q));                                  # Sub tree or not a sub tree

    $q->setReg(15);                                                             # Create a compression mask to squeeze out the key/data
    Not r15;                                                                    # Invert point
    Mov rsi, r15;                                                               # Inverted point
    And rsi, $t->keyDataMask;                                                   # Mask for keys area
    Kmovq k7, rsi;
    Vpcompressd zmmM($K, 7), zmm($K);                                           # Compress out the key
    Vpcompressd zmmM($D, 7), zmm($D);                                           # Compress out the data

    PushR 6, 31;
    $t->getTreeBits($K, rsi);                                                   # Tree bits
    Kmovq k6, rsi;
    Vpmovm2d zmm(31), k6;                                                       # Broadcast the tree bits into a zmm
    Vpcompressd zmmM(31, 7), zmm(31);                                           # Compress out the tree bit in question
    Vpmovd2m k6, zmm(31);                                                       # Reform the tree bits minus the squeezed out bit
    Kmovq rsi, k6;                                                              # New tree bits
    $t->setTreeBits($K, rsi);                                                   # Reload tree bits
    PopR;

    Mov rsi, r15;                                                               # Inverted point
    And rsi, $t->nodeMask;                                                      # Mask for node area
    Kmovq k7, rsi;
    Vpcompressd zmmM($N, 7), zmm($N);                                           # Compress out the node

    $t->decLengthInKeys($K);                                                    # Reduce length by  one

    PopR;
   } parameters => [qw(point)],
     structures => {tree=>$tree},
     name       => qq(Nasm::X86::Tree::extract-$K-$D-$N-$$tree{length}-$$tree{stringTree});

  $s->inline(structures=>{tree => $tree}, parameters=>{point => $point});
 } # extract

sub Nasm::X86::Tree::extractFirst($$$$)                                         #P Extract the first key/data and tree bit at the specified point from the block held in the specified zmm registers and place the extracted data/bit in tree data/subTree.
 {my ($tree, $K, $D, $N) = @_;                                                  # Tree descriptor, keys zmm, data zmm, node zmm
  @_ == 4 or confess "Four parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $t = $$s{tree};                                                          # Tree to search
    $t->leafFromNodes($N);                                                      # Check for a leaf
    if ($DebugMode)                                                             # Checking
     {IfNe                                                                      # If the zero Flag is zero then this is not a leaf
      Then                                                                      # We can only perform this operation on a leaf
       {PrintErrTraceBack "Cannot extract first from a non leaf node";
       };
     }
    $t->key ->copy(dFromZ($K, 0));                                              # Save corresponding key  into tree data field
    $t->data->copy(dFromZ($D, 0));                                              # Save corresponding data into tree data field

    PushR 7;
    Mov rsi, $t->keyDataMask;                                                   # Mask for keys area
    Sub rsi, 1;                                                                 # Mask for keys area with a zero in the first position
    Kmovq k7, rsi;
    Vpcompressd zmmM($K, 7), zmm($K);                                           # Compress out the key
    Vpcompressd zmmM($D, 7), zmm($D);                                           # Compress out the data

    $t->getTreeBits($K, rdi);                                                   # Tree bits
    Mov rsi, rdi;
    And rsi, 1;                                                                 # First tree bit
    $t->subTree->getReg(rsi);                                                   # Save tree bit
    Shr rdi, 1;                                                                 # Remove first tree bit
    $t->setTreeBits($K, rdi);                                                   # Reload tree bits

    $t->decLengthInKeys($K);                                                    # Reduce length by one

    PopR;
   } structures=>{tree=>$tree},
     name => qq(Nasm::X86::Tree::extractFirst-$K-$D-$N-$$tree{length}-$$tree{stringTree});

  $s->call(structures=>{tree => $tree});
 } # extractFirst

sub Nasm::X86::Tree::mergeOrSteal($$)                                           #P Merge the block at the specified offset with its right sibling or steal from it. If there is no  right sibling then do the same thing but with the left sibling.  The supplied block must not be the root. The key we are looking for must be in the tree key field.
 {my ($tree, $offset) = @_;                                                     # Tree descriptor, offset of non root block that might need to merge or steal
  @_ == 2 or confess "Two parameters";

  my $s = Subroutine
   {my ($parameters, $structures, $sub) = @_;                                   # Parameters, structures, subroutine definition

    my $t  = $$structures{tree};                                                # Tree to search
    my $F  = 31;
    my $PK = 30; my $PD = 29; my $PN = 28;
    my $LK = 27; my $LD = 26; my $LN = 25;
    my $RK = 24; my $RD = 23; my $RN = 22;

    PushR 22..31;

    my $l = $$parameters{offset}->clone("left");                                # Offset of left node that might need merging

    if ($DebugMode)                                                             # Checking
     {If $l == 0,
      Then
       {PrintErrTraceBack "Zero offset in mergeOrSteal";
       };
     }
    $t->getBlock($l, $LK, $LD, $LN);                                            # Get the keys/data/nodes
    my $p = $t->upFromData($LD);                                                # Parent offset

    if ($DebugMode)                                                             # Checking
     {If $p == 0,
      Then
       {PrintErrTraceBack "Cannot mergeOrSteal the root";
       };
     }

    my $ll = $t->lengthFromKeys($LK);                                           # Length of left
    If $ll == $t->lengthMin,                                                    # Need to merge or steal
    Then
     {$t->getBlock($p, $PK, $PD, $PN);                                          # Get the parent
      If $l != $t->lastNode($PK, $PD, $PN),
      Then                                                                      # Not the last node so we ca either steal or merge right
       {my $ll = $t->lengthFromKeys($LK);                                       # Length of left
        my $r = $t->nextNode($l, $PK, $PN);                                     # Right hand will be next sibling
        $t->getBlock($r, $RK, $RD, $RN);                                        # Get next sibling

        my $rl = $t->lengthFromKeys($RK);
        If $rl == $t->lengthMin,
        Then                                                                    # Merge left and right siblings because we now know they are both minimal
         {$t->merge($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);               # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm.
          If $t->lengthFromKeys($PK) == 0,
          Then                                                                  # We just merged in the root so make the left sibling the root
           {$t->firstFromMemory($F);
            $t->rootIntoFirst($F, $l);
            $t->firstIntoMemory($F);
            $t->upIntoData(K(zero => 0), $LD);                                  # Zero the up pointer for the root
            $t->freeBlock($p, $PK, $PD, $PN);                                   # Free parent as it is no longer needed
           },                                                                   # Else not required
          Else                                                                  # Steal from right sibling
           {$t->putBlock($p, $PK, $PD, $PN);                                    # Save modified parent
           };
          $t->freeBlock($r, $RK, $RD, $RN);                                     # Free right as it is no longer needed
         },
        Else                                                                    # Steal from right sibling
         {$t->stealFromRight($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);      # Steal
          $t->putBlock($p, $PK, $PD, $PN);                                      # Save modified parent
          $t->putBlock($r, $RK, $RD, $RN);                                      # Save modified right
         };
        $t->putBlock($l, $LK, $LD, $LN);                                        # Save non minimum left
        $$parameters{changed}->copy(1);                                         # Show that we changed the tree layout
       },

      Else                                                                      # Left sibling is last so we either merge the two nodes to eliminate the right node or steal from the left is that is not possible
       {my $r = $l;                                                             # The left sibling is last so we make it the right block
        $t->getBlock($r, $RK, $RD, $RN);                                        # Get the right keys/data/nodes
        my $l = $t->prevNode($r, $PK, $PN);                                     # Left block will be previous sibling
        $t->getBlock($l, $LK, $LD, $LN);                                        # Get the right keys/data/nodes
        my $ll = $t->lengthFromKeys($LK);                                       # Length of left
        If $ll == $t->lengthMin,                                                # Has the the bare minimum so must merge or steal
        Then
         {$t->merge($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);               # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm.
          If $t->lengthFromKeys($PK) == 0,
          Then                                                                  # We just merged in the root so make the left sibling the root
           {$t->firstFromMemory($F);
            $t->rootIntoFirst($F, $l);
            $t->firstIntoMemory($F);
            $t->upIntoData(K(zero => 0), $LD);                                  # Zero the up pointer for the root
            $t->freeBlock($p, $PK, $PD, $PN);                                   # Free parent as it is no longer needed
           },                                                                   # Else not required
          Else                                                                  # Steal from right sibling
           {$t->putBlock($p, $PK, $PD, $PN);                                    # Save modified parent
           };
           $t->freeBlock($r, $RK, $RD, $RN);                                    # Save modified right
         },
        Else                                                                    # Steal from right sibling
         {$t->stealFromLeft($PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN);       # Steal
          $t->putBlock($p, $PK, $PD, $PN);                                      # Save modified parent
          $t->putBlock($r, $RK, $RD, $RN);                                      # Save modified right
         };
        $t->putBlock($l, $LK, $LD, $LN);                                        # Save non minimum left
        $$parameters{changed}->copy(1);                                         # Show that we changed the tree layout
       };
     };
    PopR;
   } parameters => [qw(offset changed)],
     structures => {tree=>$tree},
     name       => qq(Nasm::X86::Tree::mergeOrSteal-$$tree{length}-$$tree{stringTree});

  $s->call
   (structures => {tree   => $tree},
    parameters => {offset => $offset, changed => my $changed = V changed => 0});

  $changed                                                                      # Whether we did a merge or steal
 } # mergeOrSteal

sub Nasm::X86::Tree::stealFromRight($$$$$$$$$$)                                 #P Steal one key from the node on the right where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else zero.
 {my ($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN) = @_;                 # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm, right keys, data, nodes zmm.
  @_ == 10 or confess "Ten parameters required";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    my $t  = $$s{tree};
    my $ll = $t->lengthFromKeys($LK);
    my $lr = $t->lengthFromKeys($RK);

    PushR 7;

    $t->found->copy(0);                                                         # Assume we cannot steal

    Block                                                                       # Check that it is possible to steal key a from the node on the right
     {my ($end, $start) = @_;                                                   # Code with labels supplied
      If $ll != $t->lengthLeft,
      Then                                                                      # Left not minimal
       {PrintErrStringNL "Left not minimal";
        Jmp $end
       };
      If $lr == $t->lengthRight,                                                # Right minimal
      Then
       {PrintErrStringNL "Should merge not steal";
        Jmp $end
       };

      $t->found->copy(1);                                                       # Proceed with the steal

      my $pir = (K one => 1);                                                   # Point of right key to steal
      my $pil = $pir << ($ll - 1);                                              # Point of left key to receive key

      my $rk  = $pir->dFromPointInZ($RK);                                       # Right key to rotate left
      my $rd  = $pir->dFromPointInZ($RD);                                       # Right data to rotate left
      my $rn  = $pir->dFromPointInZ($RN);                                       # Right node to rotate left

      If $t->leafFromNodes($LN) == 0,
      Then                                                                      # Left is not a leaf so the right is not a leaf so we must upgrade first right child up pointer
       {PushR $LK, $LD, $LN, $RK, $RD, $RN;
        my $ln = dFromZ($LN, 0);                                                # First child of left
        $t->getBlock($ln, $LK, $LD, $LN);                                       # Left grand child
        $t->getBlock($rn, $RK, $RD, $RN);                                       # Right grand child
        my $lcu = $t->upFromData($LD);                                          # Offset of left block
        $t->upIntoData($lcu, $RD);                                              # Set up of right grand child to left block
        $t->putBlock($rn, $RK, $RD, $RN);
        PopR;
       };

      my $pip = $t->insertionPoint($rk, $PK);                                   # Point of parent key to insert
      my $pip1= $pip >> K(one=>1);                                              # Point of parent key to merge in
      my $pk  = $pip1->dFromPointInZ($PK);                                      # Parent key to rotate left
      my $pd  = $pip1->dFromPointInZ($PD);                                      # Parent data to rotate left

      my $pb  = $t->getTreeBit($PK, $pip);                                      # Parent tree bit
      my $rb  = $t->getTreeBit($RK, K one => 1);                                # First right tree bit
      $pip1->dIntoPointInZ($PK, $rk);                                           # Right key into parent
      $pip1->dIntoPointInZ($PD, $rd);                                           # Right data into parent
      $t->setOrClearTreeBitToMatchContent($PK, $pip, $rb);                      # Right tree bit into parent
      $pk->dIntoZ($LK, $t->middleOffset);                                       # Parent key into left
      $pd->dIntoZ($LD, $t->middleOffset);                                       # Parent data into left
      $rn->dIntoZ($LN, $t->rightOffset);                                        # Right node into left

      $t->insertIntoTreeBits($LK, K(position => 1 << $t->lengthLeft), $pb);     # Parent tree bit into left

      LoadConstantIntoMaskRegister                                              # Nodes area
       (7, createBitNumberFromAlternatingPattern '00', $t->maxKeysZ-1, -1);
      Vpcompressd zmmM($RK, 7), zmm($RK);                                       # Compress right keys one slot left
      Vpcompressd zmmM($RD, 7), zmm($RD);                                       # Compress right data one slot left

      LoadConstantIntoMaskRegister                                              # Nodes area
       (7, createBitNumberFromAlternatingPattern '0', $t->maxNodesZ-1, -1);
      Vpcompressd zmmM($RN, 7), zmm($RN);                                       # Compress right nodes one slot left

      $t->incLengthInKeys($LK);                                                 # Increment left hand length
      $t->decLengthInKeys($RK);                                                 # Decrement right hand
     };
    PopR;
   }
  name       => join('::',
   "Nasm::X86::Tree::stealFromRight",
    $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN, $tree->length),
  structures => {tree => $tree};

  $s->call(structures => {tree   => $tree});

  $tree                                                                         # Chain
 }

sub Nasm::X86::Tree::stealFromLeft($$$$$$$$$$)                                  #P Steal one key from the node on the left where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else  zero.
 {my ($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN) = @_;                 # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm, right keys, data, nodes zmm.
  @_ == 10 or confess "Ten parameters required";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    my $t  = $$s{tree};
    my $ll = $t->lengthFromKeys($LK);
    my $lr = $t->lengthFromKeys($RK);

    PushR 7;

    $t->found->copy(0);                                                         # Assume we cannot steal

    Block                                                                       # Check that it is possible to steal a key from the node on the left
     {my ($end, $start) = @_;                                                   # Code with labels supplied
      If $lr != $t->lengthRight,  Then {Jmp $end};                              # Right not minimal
      If $ll == $t->lengthLeft,   Then {Jmp $end};                              # Left minimal

      $t->found->copy(1);                                                       # Proceed with the steal

      my $pir = K(one => 1);                                                    # Point of right key
      my $pil = $pir << ($ll - 1);                                              # Point of left key

      my $lk  = $pil->dFromPointInZ($LK);                                       # Left key to rotate right
      my $ld  = $pil->dFromPointInZ($LD);                                       # Left data to rotate right
      my $ln  = ($pil << K(key => 1))->dFromPointInZ($LN);                      # Left node to rotate right

      my $lb  = $t->getTreeBit($LK, $pil);                                      # Left tree bit to rotate right

      my $pip = $t->insertionPoint($lk, $PK);                                   # Point of parent key to merge in

      my $pk  = $pip->dFromPointInZ($PK);                                       # Parent key to rotate right
      my $pd  = $pip->dFromPointInZ($PD);                                       # Parent data to rotate right
      my $pb  = $t->getTreeBit($PK, $pip);                                      # Parent tree bit

      LoadConstantIntoMaskRegister                                              # Nodes area
       (7, createBitNumberFromAlternatingPattern '00', $t->maxKeysZ-1, -1);
      Vpexpandd zmmM($RK, 7), zmm($RK);                                         # Expand right keys one slot right
      Vpexpandd zmmM($RD, 7), zmm($RD);                                         # Expand right data one slot right

      LoadConstantIntoMaskRegister                                              # Nodes area
       (7, createBitNumberFromAlternatingPattern '0', $t->maxNodesZ-1, -1);
      Vpexpandd zmmM($RN, 7), zmm($RN);                                         # Expand right nodes one slot right

      $pip->dIntoPointInZ($PK, $lk);                                            # Left key into parent
      $pip->dIntoPointInZ($PD, $ld);                                            # Left data into parent
      $t->setOrClearTreeBitToMatchContent($PK, $pip, $lb);                      # Left tree bit into parent

      $pir->dIntoPointInZ($RK, $pk);                                            # Parent key into right
      $pir->dIntoPointInZ($RD, $pd);                                            # Parent data into right
      $pir->dIntoPointInZ($RN, $ln);                                            # Left node into right
      $t->insertIntoTreeBits($RK, $pir, $pb);                                   # Parent tree bit into right

      $t->decLengthInKeys($LK);                                                 # Decrement left hand
      $t->incLengthInKeys($RK);                                                 # Increment right hand

      If $t->leafFromNodes($RN) == 0,
      Then                                                                      # Right is not a leaf so we must upgrade the up pointer of the first child of right to match that of the second child of right
       {PushR $LK, $LD, $LN, $RK, $RD, $RN;
        my $r1 = dFromZ($RN, 0);                                                # First child of right
        my $r2 = dFromZ($RN, 0);                                                # Second child of right
        $t->getBlock($r1, $LK, $LD, $LN);                                       # Load first child of right
        $t->getBlock($r2, $RK, $RD, $RN);                                       # Load second child of right
        my $r2u = $t->upFromData($RD);                                          # Up from second child of right
        $t->upIntoData($r2u, $LD);                                              # Set first child up to second child up
        $t->putBlock($r1, $LK, $LD, $LN);
        PopR;
       };

     };
    PopR;
   }
  name       => join('::',
   "Nasm::X86::Tree::stealFromLeft",
    $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN, $tree->length),

  structures => {tree => $tree};

  $s->call(structures => {tree   => $tree});

  $tree                                                                         # Chain
 } # stealFromLeft

sub Nasm::X86::Tree::merge($$$$$$$$$$)                                          #P Merge a left and right node if they are at minimum size.
 {my ($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN) = @_;                 # Tree definition, parent keys zmm, data zmm, nodes zmm, left keys zmm, data zmm, nodes zmm, right keys, data, nodes zmm.
  @_ == 10 or confess "Ten parameters required";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    my $t  = $$s{tree};
    my $ll = $t->lengthFromKeys($LK);
    my $lr = $t->lengthFromKeys($RK);

    PushR 7, 14, 15;

    Block                                                                       # Check that it is possible to steal a key from the node on the left
     {my ($end, $start) = @_;                                                   # Code with labels supplied
      If $ll != $t->lengthLeft,  Then {Jmp $end};                               # Left not minimal
      If $lr != $t->lengthRight, Then {Jmp $end};                               # Right not minimal

      my $pil = K(one => 1);                                                    # Point of first left key
      my $lk  = $pil->dFromPointInZ($LK);                                       # First left key
      my $pip = $t->insertionPoint($lk, $PK);                                   # Point of parent key to merge in
      my $pk  = $pip->dFromPointInZ($PK);                                       # Parent key to merge
      my $pd  = $pip->dFromPointInZ($PD);                                       # Parent data to merge
      my $pn  = $pip->dFromPointInZ($PN);                                       # Parent node to merge
      my $pb  = $t->getTreeBit($PK, $pip);                                      # Parent tree bit

      my $m = K(one => 1) << K( shift => $t->lengthLeft);                       # Position of parent key in left
      $m->dIntoPointInZ($LK, $pk);                                              # Position parent key in left
      $m->dIntoPointInZ($LD, $pd);                                              # Position parent data in left
      $t->insertIntoTreeBits($LK, $m, $pb);                                     # Tree bit for parent data
      LoadConstantIntoMaskRegister                                              # Keys/Data area
       (7, createBitNumberFromAlternatingPattern '00', $t->lengthRight,   -$t->lengthMiddle);
      Vpexpandd zmmM($LK, 7), zmm($RK);                                         # Expand right keys into left
      Vpexpandd zmmM($LD, 7), zmm($RD);                                         # Expand right data into left
      LoadConstantIntoMaskRegister                                              # Nodes area
       (7, createBitNumberFromAlternatingPattern '0',  $t->lengthRight+1, -$t->lengthMiddle);
      Vpexpandd zmmM($LN, 7), zmm($RN);                                         # Expand right data into left

      $pip->setReg(15);                                                         # Collapse mask for keys/data in parent
      Not r15;
      And r15, $t->treeBitsMask;
      Kmovq k7, r15;
      Vpcompressd zmmM($PK, 7), zmm($PK);                                       # Collapse parent keys
      Vpcompressd zmmM($PD, 7), zmm($PD);                                       # Collapse data keys

      my $one = K(one => 1);                                                    # Collapse mask for keys/data in parent
#     my $np = (!$pip << $one) >> $one;
      my $np = !$pip << $one;                                                   # Move the compression point up one to remove the matching node
      $np->setReg(14);
      Add r14, 1;                                                               # Fill hole left at position 0
      Kmovq k7, r14;                                                            # Node squeeze mask
      Vpcompressd zmmM($PN, 7), zmm($PN);                                       # Collapse nodes

      my $z = $PK == 31 ? 30: 31;                                               # Collapse parent tree bits
      PushR zmm $z;                                                             # Collapse parent tree bits
      $t->getTreeBits($PK, r15);                                                # Get tree bits
      Kmovq k7, r15;                                                            # Tree bits
      Vpmovm2d zmm($z), k7;                                                     # Broadcast the bits into a zmm
      $pip->setReg(15);                                                         # Parent insertion point
      Kmovq k7, r15;
      Knotq k7, k7;                                                             # Invert parent insertion point
      Vpcompressd zmmM($z, 7), zmm($z);                                         # Compress
      Vpmovd2m k7, zmm $z;                                                      # Recover bits
      Kmovq r15, k7;
      And r15, $t->treeBitsMask;                                                # Clear trailing bits beyond valid tree bits
      $t->setTreeBits($PK, r15);
      PopR;

      $t->getTreeBits($LK, r15);                                                # Append right tree bits to the Left tree bits
      $t->getTreeBits($RK, r14);                                                # Right tree bits
      my $sl = RegisterSize(r15) * $bitsInByte / 4 - $tree->lengthMiddle;       # Clear bits right of the lower left bits
      Shl r15w, $sl;
      Shr r15w, $sl;

      Shl r14, $tree->lengthMiddle;                                             # Move right tree bits into position
      Or  r15, r14;                                                             # And in left tree bits
      And r15, $t->treeBitsMask;                                                # Clear trailing bits beyond valid tree bits
      $t->setTreeBits($LK, r15);                                                # Set tree bits

      If $t->leafFromNodes($RN) == 0,
      Then                                                                      # Right is not a leaf so we must upgrade the up offset of its children to the up pointer of the first left child
       {PushR $LK, $LD, $LN;
        my $l1 = dFromZ($LN, 0);                                                # First child of left
        $t->getBlock($l1, $LK, $LD, $LN);                                       # Load first child of left
        my $l2u = $t->upFromData($LD);                                          # Offset of left block
        my $lr = 1 + $t->lengthFromKeys($RK);                                   # Number of right children
        $lr->for(sub                                                            # Each child of right
         {my ($i) = @_;
          my $r = dFromZ($RN, $i * $tree->width);                               # Offset of child
          $t->getBlock($r, $LK, $LD, $LN);                                      # Load child of right
          $t->upIntoData ($l2u, $LD);                                           # Set parent
          $t->putBlock($r, $LK, $LD, $LN);                                      # Write back into memory
         });
        PopR;
       };

      $t->decLengthInKeys($PK);                                                 # Parent now has one less
      $t->lengthIntoKeys($LK, K length => $t->length);                          # Left is now full

     };
    PopR;
   }
  name       => join('::',
   "Nasm::X86::Tree::merge",
    $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN, $tree->length),
  structures => {tree => $tree};

  $s->call(structures => {tree=> $tree});

  $tree                                                                         # Chain
 } # merge

sub Nasm::X86::Tree::deleteFirstKeyAndData($$$)                                 #P Delete the first element of a leaf mode returning its characteristics in the calling tree descriptor.
 {my ($tree, $K, $D) = @_;                                                      # Tree definition, keys zmm, data zmm
  @_ == 3 or confess "Three parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    my $t = $$s{tree};
    my $l = $t->lengthFromKeys($K);

    PushR 7, 14, 15;

    $t->found->copy(0);                                                         # Assume not found

    Block                                                                       # Check that it is possible to steal a key from the node on the left
     {my ($end, $start) = @_;                                                   # Code with labels supplied
      If $l == 0,  Then {Jmp $end};                                             # No elements left

      $t->found->copy(1);                                                       # Show first key and data have been found

      $t->key ->copy(dFromZ $K, 0);                                             # First key
      $t->data->copy(dFromZ $D, 0);                                             # First data
      $t->getTreeBits($K, r15);                                                 # First tree bit

      Mov r14, r15;
      Shr r14, 1;                                                               # Shift tree bits over by 1
      $t->setTreeBits($K, r14);                                                 # Save new tree bits
      And r15, 1;                                                               # Isolate first tree bit
      $t->subTree->copy(r15);                                                   # Save first tree bit

      my $m = (K(one => 1) << K(shift => $t->length)) - 2;                      # Compression mask to remove key/data
      $m->setReg(7);
      Vpcompressd zmmM($K, 7), zmm($K);                                         # Compress out first key
      Vpcompressd zmmM($D, 7), zmm($D);                                         # Compress out first data

      $t->decLengthInKeys($K);                                                  # Reduce length
     };
    PopR;
   }
  name => qq(Nasm::X86::Tree::deleteFirstKeyAndData-$K-$D-$$tree{length}-$$tree{stringTree}),
  structures => {tree => $tree};

  $s->call(structures => {tree => $tree});

  $tree                                                                         # Chain tree - actual data is in key, data,  subTree, found variables
 }

sub Nasm::X86::Tree::delete($$)                                                 # Find a key in a tree and delete it returning he value of the l=key deleted if found.
 {my ($tree, $key) = @_;                                                        # Tree descriptor, key field to delete
  @_ == 2 or confess "Two parameters";

  confess "No yet implemented for stringTrees" if $tree->stringTree;

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    Block
     {my ($success) = @_;                                                       # Short circuit if ladders by jumping directly to the end after a successful push

      my $t = $$s{tree}->zero;                                                  # Tree to search
      my $k = $$p{key};                                                         # Key to find
      $t->key->copy($k);                                                        # Copy in key so we know what was searched for

      $t->find($k);                                                             # See if we can find the key
      If $t->found == 0, Then {Jmp $success};                                   # Key not present so we cannot delete

      PushR my $F = 31, my $K = 30, my $D = 29, my $N = 28;

      $t->firstFromMemory($F);                                                  # Update the size of the tree
      my $size = $t->sizeFromFirst($F);                                         # Size of tree
      $t->decSizeInFirst($F);
      $t->firstIntoMemory($F);

      K(loop => 99)->for(sub
       {my ($i, $startDescent, $next, $end) = @_;

        $t->firstFromMemory         ($F);                                       # Load first block
        my $root = $t->rootFromFirst($F);                                       # Start the search from the root to locate the  key to be deleted
        If $root == 0, Then{Jmp $success};                                      # Empty tree so we have not found the key and nothing needs to be done

        If $size == 1,                                                          # Delete the last element which must be the matching element
        Then
         {$t->rootIntoFirst($F, K z=>0);                                        # Empty the tree
          $t->firstIntoMemory($F);                                              # The position of the key in the root node
          Jmp $success
         };

        $t->getBlock($root, $K, $D, $N);                                        # Load root block
        If $t->leafFromNodes($N) > 0,                                           # Element must be in the root as the root is a leaf and we know the key can be found
        Then
         {my $eq = $t->indexEq($k, $K);                                         # Key must be in this leaf as we know it can be found and this is the last opportunity to find it
          $t->extract($eq, $K, $D, $N);                                         # Extract from root
          $t->putBlock($root, $K, $D, $N);
          Jmp $success
         };

        my $P = $root->clone('position');                                       # Position in tree
        K(loop => 99)->for(sub                                                  # Step down through tree looking for the key
         {my ($index, $start, $next, $end) = @_;
          my $eq = $t->indexEq($k, $K);                                         # The key might still be in the parent now known not be a leaf
          If $eq > 0,
          Then                                                                  # We have found the key so now we need to find the next leaf unless this node is in fact a leaf
           {my $pu = $t->upFromData($D);                                        # Parent offset
            If $pu > 0,
            Then                                                                # Cannot merge or steal on root
             {If $t->mergeOrSteal($P) > 0,                                      # Merge or steal if necessary
              Then                                                              # Restart entire process because we might have changed the position of the key being deleted by merging in its vicinity
               {Jmp $startDescent;
               };
             };

            If $t->leafFromNodes($N) > 0,                                       # We found the item in a leaf so it can be deleted immediately if there is enough
            Then
             {my $eq = $t->indexEq($k, $K);                                     # Key must be in this leaf as we know it can be found and this is the last opportunity to find it
              $t->extract($eq, $K, $D, $N);                                     # Remove from block
              $t->putBlock($P, $K, $D, $N);                                     # Save block
              Jmp $success;                                                     # Leaf removed
             };

            my $eq = $t->indexEq($k, $K);                                       # Location of key
            my $Q = ($eq << K(one=>1))->dFromPointInZ($N);                      # Go right to the next level down

            K(loop => 99)->for(sub                                              # Find the left most leaf
             {my ($index, $start, $next, $end) = @_;

              If $t->mergeOrSteal($Q) > 0,                                      # Merge or steal if necessary
              Then                                                              # Restart entire process because we might have changed the position of the key being deleted by merging in its vicinity
               {Jmp $startDescent;
               };
              $t->getBlock($Q, $K, $D, $N);                                     # Next block down
              If $t->leafFromNodes($N) > 0,                                     # We must hit a leaf eventually
              Then
               {$t->extractFirst($K, $D, $N);                                   # Remove from block
                $t->putBlock($Q, $K, $D, $N);                                   # Save block

                my $key     = $t->key->clone("key");                            # Record details of leaf
                my $data    = $t->data->clone("data");
                my $subTree = $t->subTree->clone("data");
                $t->find($k);                                                   # Find key we actually want to delete

                $t->key    ->copy($key);                                        # Reload
                $t->data   ->copy($data);
                $t->subTree->copy($subTree);

                my $l = $t->offset;                                             # Offset of block containing key

                $t->getBlock($l, $K, $D, $N);                                   # Block containing key
                $t->replace ($t->found,  $K, $D);                               # Replace key to delete with leaf
                $t->putBlock($l, $K, $D, $N);                                   # Save block
                Jmp $success;
               };

              my $i = $t->insertionPoint($k, $K);                               # The insertion point if we were inserting is the next node to visit
              $Q->copy($i->dFromPointInZ($N));                                  # Get the corresponding offset of the the next block down
             });
             Jmp $success;
           };

          my $i = $t->insertionPoint($k, $K);                                   # The insertion point if we were inserting is the next node to visit
          $P->copy($i->dFromPointInZ($N));                                      # Get the corresponding node

          $t->getBlock($P, $K, $D, $N);                                         # Get the next block

          my $l = $t->lengthFromKeys($K);                                       # Length of block

          If $l == $t->lengthMin,                                               # Has the the bare minimum so must be merged.
          Then
           {If $t->mergeOrSteal($P) > 0,                                        # Merge or steal if necessary
            Then                                                                # Restart entire process because we might have changed the position of the key being deleted by merging in its vicinity
             {Jmp $startDescent;
             };
           };
         });
       });
      PrintErrTraceBack "Stuck looking for leaf" if $DebugMode;
     };                                                                         # Find completed successfully
    PopR;
   } parameters =>[qw(key)],
     structures =>{tree=>$tree},
     name       => qq(Nasm::X86::Tree::delete-$$tree{length}-$$tree{stringTree});

  $s->call(structures => {tree => $tree},
           parameters => {key  => ref($key) ? $key : K key =>  $key});
 } # delete

sub Nasm::X86::Tree::clear($)                                                   # Delete everything in the tree except the first block recording any memory liberated on the free chain.
 {my ($tree) = @_;                                                              # Tree
  @_ == 1 or confess "One parameter";

  my $s = Subroutine                                                            # Delete all the sub blocks of a block and then free the block as well
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $t = $$s{tree};                                                          # Tree
    my $area = $t->area;                                                        # Area

    PushR my $K = 31, my $D = 30, my $N = 29;

    Block                                                                       # Free sub blocks then free block
     {my ($end, $start) = @_;

      $t->getBlock($$p{offset}, $K, $D, $N);                                    # Load block

      If $t->leafFromNodes($N) == 0,
      Then                                                                      # Not a leaf so free the sub blocks
       {my $l = $t->lengthFromKeys($K);                                         # Number of nodes
        ($l+1)->for(sub                                                         # Free sub blocks
         {my ($i) = @_;
          $sub->call(parameters => {offset => dFromZ $N, $i * $t->width},       # Recurse
                     structures => {tree   => $t});
         });
       };

      $t->freeBlock($$p{offset}, $K, $D, $N);                                   # Free this block
     };

    PopR;
   } parameters => [qw(offset)],
     structures => {tree => $tree},
     name       => qq(Nasm::X86::Tree::clear-$$tree{length}-$$tree{stringTree});

  PushR my $F = 31;
  $tree->firstFromMemory($F);
  my $root = $tree->rootFromFirst($F);                                          # Root block if any

  If $root > 0,                                                                 # Non empty tree
  Then
   {$s->call(structures => {tree  => $tree}, parameters => {offset => $root});  # Free from root node
    $tree->rootIntoFirst($F, K root => 0);
    $tree->sizeIntoFirst($F, K size => 0);
    $tree->firstIntoMemory($F);
   };

  PopR;
 }

sub Nasm::X86::Tree::free($)                                                    # Free all the memory used by a tree.
 {my ($tree) = @_;                                                              # Tree
  @_ == 1 or confess "One parameter";
  $tree->clear;                                                                 # Clear the tree
 }

#D2 Iteration                                                                   # Iterate through a tree non recursively

sub Nasm::X86::Tree::by($&)                                                     # Call the specified block with each element of the specified tree in ascending order.
 {my ($tree, $block) = @_;                                                      # Tree descriptor, block to execute
  @_ == 2 or confess "Two parameters required";

  $tree->findFirst;                                                             # First element
  my $end   = Label;                                                            # End of processing
  my $next  = Label;                                                            # Next iteration
  my $start = SetLabel;                                                         # Start of this iteration
  If $tree->found == 0, Then {Jmp $end};
  &$block($tree, $start, $next, $end);                                          # Perform the specified block
  SetLabel $next;
  $tree->findNext($tree->key);
  Jmp $start;
  SetLabel $end;
 }

sub Nasm::X86::Tree::yb($&)                                                     # Call the specified block with each element of the specified tree in descending order.
 {my ($tree, $block) = @_;                                                      # Tree descriptor, block to execute
  @_ == 2 or confess "Two parameters required";

  $tree->findLast;                                                              # Last element
  my $end   = Label;                                                            # End of processing
  my $prev  = Label;                                                            # Next iteration
  my $start = SetLabel;                                                         # Start of this iteration
  If $tree->found == 0, Then {Jmp $end};
  &$block($tree, $start, $prev, $end);                                          # Perform the specified block
  SetLabel $prev;
  $tree->findPrev($tree->key);
  Jmp $start;
  SetLabel $end;
 }

#D2 Push and Pop                                                                # Use a tree as a stack: Push elements on to a tree with the next available key; Pop the last element in a tree.

sub Nasm::X86::Tree::push($$)                                                   #P Push a data value onto a tree. If the data is a reference to a tree then the offset of the first block of the tree is pushed.
 {my ($tree, $data) = @_;                                                       # Tree descriptor, variable data
  @_ == 2 or confess "Two parameters";

  $tree->findLast;                                                              # Last element
  If $tree->found == 0,
  Then                                                                          # Empty tree
   {$tree->put(K(key => 0), $data);                                             # First element in tree
   },
  Else                                                                          # Non empty tree
   {$tree->put($tree->key + 1, $data);                                          # Last element in tree
   };
 }

sub Nasm::X86::Tree::peek($$)                                                   # Peek at the element the specified distance back from the top of the stack and return its B<value> in data and found status in B<found> in the tree descriptor.
 {my ($tree, $Back) = @_;                                                       # Tree descriptor, how far back to go with 1 being the top
  @_ == 2 or confess "Two parameters";

  $tree->found->copy(0);                                                        # Assume we will not be able to find the desired element

  my $size = $tree->size;                                                       # Size of the stack
  my $back = ref($Back) ? $Back : K back => $Back;
  If $back <= $size,
  Then                                                                          # Requested element is available on the stack
   {$tree->find($size - $back);
   };
  $tree
 }

sub Nasm::X86::Tree::peekSubTree($$)                                            # Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.
 {my ($tree, $Back) = @_;                                                       # Tree descriptor, how far back to go with 1 being the top
  @_ == 2 or confess "Two parameters";
  my $back = ref($Back) ? $Back : K back => $Back;

  my $t = $tree->DescribeTree;                                                  # Create a tree descriptor
  $t->found->copy(0);                                                           # Mark tree as not set
  $tree->peek($back);                                                           # Requested element
  If $tree->found > 0,
  Then                                                                          # Found an element
   {If $tree->subTree > 0,
    Then                                                                        # Found a sub tree
     {$t->first->copy($tree->data);                                             # Reposition on sub tree
      $t->found->copy(1);
     };
   };
  $t
 }

sub Nasm::X86::Tree::pop($)                                                     # Pop the last value out of a tree and return the key/data/subTree in the tree descriptor.
 {my ($tree) = @_;                                                              # Tree descriptor
  @_ == 1 or confess "One parameter";

  $tree->findLast;                                                              # Last element
  If $tree->found > 0,
  Then                                                                          # Empty tree
   {my $k = $tree->key    ->clone('key');
    my $d = $tree->data   ->clone('data');
    my $s = $tree->subTree->clone('subTree');
    $tree->delete($k);                                                          # Delete last key
    $tree->key    ->copy($k);                                                   # Retrieved key
    $tree->data   ->copy($d);                                                   # Retrieved data
    $tree->subTree->copy($s);                                                   # Retrieved sub tree indicator
    $tree->found  ->copy(1);                                                    # Indicate success
   },
  Else
   {PrintErrTraceBack "Empty stack";
   };
 }

sub Nasm::X86::Tree::popSubTree($%)                                             # Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.
 {my ($tree, %options) = @_;                                                    # Tree descriptor, options describing the sub tree
  @_ >= 1 or confess "One or more parameter";

  $tree->pop;

  my $t = $tree->DescribeTree(%options);                                        # Create a tree descriptor to indicate the result
     $t->zero;
  If $tree->found > 0,
  Then
   {If $tree->subTree > 0,
    Then
     {$t->found->copy($tree->found);
      $t->first->copy($tree->data);                                             # We are popping a tree so the data is the offset of the first block
      $t->subTree->copy(1);
     },
    Else
     {PrintErrTraceBack "Not a sub tree";
     };
   };

  $t                                                                            # Sub tree
 }

sub Nasm::X86::Tree::get($$)                                                    # Retrieves the element at the specified zero based index in the stack.
 {my ($tree, $Key) = @_;                                                        # Tree descriptor, zero based index
  @_ == 2 or confess "Two parameters";
  my $key  = ref($Key)  ? $Key : K(key => $Key);                                # Promote constant

  $tree->find($key);
  $tree->key->copy($key);
 }

#D2 Trees as Strings                                                            # Use trees as strings of dwords.  The size of the tree is the length of the string. Each dword is consider as an indivisible unit. This arrangement allows the normal string operations of concatenation and substring to be performed easily.

sub Nasm::X86::Tree::appendAscii($$$)                                           # Append ascii bytes in memory to a tree acting as a string. The address and size of the source memory are specified via variables. Each byte should represent a valid ascii byte so that it can be considered, when left extended with 24 zero bits, as utf32.
 {my ($string, $address, $size) = @_;                                           # Tree descriptor of string to append to, variable address of memory to append from, variable size of memory
  @_ == 3 or confess "Three parameters";

  my $s = Subroutine
   {my ($parameters, $structures, $sub) = @_;
    PushR rax, 13, 14, 15;
    my $s = $$structures{string};
    $$parameters{address}->setReg(r13);
    $$parameters{size}   ->setReg(r14);
    ClearRegisters r15;
    For                                                                         # Clear memory
     {Mov al, "[r13+r15]";
      $s->push(V byte => rax);
     } r15, r14, 1;
    PopR;
   } structures => {string=>$string},
     parameters => [qw(address size)],
     name       =>  qq(Nasm::X86::Tree::m::$$string{length});

  $s->call(parameters=>{address => $address, size=>$size},
           structures=>{string=>$string});
 }

sub Nasm::X86::Tree::append($$)                                                 # Append the second source string to the first target string renumbering the keys of the source string to follow on from those of the target string.  A string can safely be appended to itself.
 {my ($string, $append) = @_;                                                   # Tree descriptor of string to append to, tree descriptor of string to append from
  @_ == 2 or confess "Two parameters";

  my $lt = $string->size;                                                       # Target string size
  my $ls = $append->size;                                                       # Source string size
  $ls->for(sub                                                                  # Look up each character
   {my ($i, $start, $next, $end) = @_;
    $append->get($i);
    $string->put($lt+$i, $append->data);
   });
  $string                                                                       # Chain from the target string
 }

sub Nasm::X86::Tree::clone($)                                                   # Clone a string.
 {my ($string) = @_;                                                            # Tree descriptor
  @_ == 1 or confess "One parameter";

  my $t = $string->area->CreateTree;                                            # Cloned copy
  $string->by(sub
   {$t->put($string->key, $string->data);
   });
  $t                                                                            # Chain from the target string
 }

sub Nasm::X86::Tree::substring($$$)                                             # Create the substring of the specified string between the specified start and end keys.
 {my ($string, $Start, $Finish) = @_;                                           # Tree descriptor of string to extract from, start key, end key
  @_ == 3 or confess "Three parameters";

  my $start  = ref($Start)  ? $Start  : K(start  => $Start);                    # Promote constant
  my $finish = ref($Finish) ? $Finish : K(finish => $Finish);                   # Promote constant

  my $t = $string->area->CreateTree;                                            # Cloned copy
  If $start == $finish,
  Then                                                                          # Start and end are the same
   {$string->find($start);
    If $string->found > 0,
    Then
     {$t->put($string->key, $string->data);
     };
   },
  Ef {$start < $finish}
  Then                                                                          # Range of several keys
   {$string->find($finish-1);
    If $string->found > 0,
    Then                                                                        # Finish exists
     {$string->find($start);
      If $string->found > 0,
      Then                                                                      # Start exists
       {$string->size->for(sub                                                  # Each key in range
         {my ($i, $start, $next, $end) = @_;
          $t->put($i, $string->data);
          $string->findNext($string->key);
          If $string->found == 0, Then {Jmp $end};                              # End of input string
          If $string->key >= $finish, Then {Jmp $end};                          # End of range
         });
       };
     };
   };
  $t                                                                            # Chain from the target string
 }

sub Nasm::X86::Tree::reverse($)                                                 # Create a clone of the string in reverse order.
 {my ($string) = @_;                                                            # Tree descriptor of string
  @_ == 1 or confess "One parameter";

  my $t = $string->area->CreateTree;                                            # Cloned reversed copy
  my $l = $string->size;                                                        # Size of string
  $string->by(sub
   {$t->put($l - $string->key - 1, $string->data);
   });
  $t                                                                            # Chain from the target string
 }

#sub Nasm::X86::Area::treeFromString($$$)                                        # Create a tree from a string of bytes held at a variable address with a variable length and return the resulting tree.  The first element of the tree is the specified length, in bytes, of the string.
# {my ($area, $address, $size) = @_;                                             # Area description, address of string, length of string in bytes
#  @_ == 3 or confess "Three parameters";
#
#  my $t = $area->CreateTree;                                                    # Create a tree to be used to store the string
#
#  PushR my $c = r13, my $a = r14, my $i = r15;
#
#  ClearRegisters $i;
#  $address->setReg($a);
#
#  $size->for(sub                                                                # Push each byte of the input string into the tree
#   {Mov $c."b", "[r14+r15]";                                                    # Load byte
#    $t->push(V chunk => $c);                                                    # Push byte into string
#    Inc $i;
#   });
#  PopR;
#
#  $t                                                                            # Description of tree loaded from string
# }

#D2 Trees as sets                                                               # Trees of trees as sets

sub Nasm::X86::Tree::union($)                                                   # Given a tree of trees consider each sub tree as a set and form the union of all these sets as a new tree.
 {my ($tree) = @_;                                                              # Tree descriptor for a tree of trees
  @_ == 1 or confess "One parameter";

  my $u = $tree->area->CreateTree;
  $tree->by(sub                                                                 # Each sub tree
   {my ($T) = @_;
    my $t = $tree->position($T->data);
    $t->by(sub                                                                  # Insert each element of each sub tree
     {my ($s) = @_;
      $u->put($s->key, $s->data);
     });
   });
  $u                                                                            # Union
 }

sub Nasm::X86::Tree::intersection($)                                            # Given a tree of trees consider each sub tree as a set and form the intersection of all these sets as a new tree.
 {my ($tree) = @_;                                                              # Tree descriptor for a tree of trees
  @_ == 1 or confess "One parameter";

  my $i = $tree->area->CreateTree;                                              # Resulting intersection
  my $F = V smallest => -1;
  my $S = V size     => -1;

  $tree->by(sub                                                                 # Find smallest sub tree
   {my ($T, $start, $next) = @_;
    my $f = $T->data;
    my $t = $tree->position($f);
    my $s = $t->size;
    OrBlock                                                                     # Update size if no size seen yet or if the size is smaller
     {my ($pass) = @_;
      If $S == -1, Then {Jmp $pass};                                            # No size set yet
      If $S > $s,  Then {Jmp $pass};                                            # Smaller size
     }                                                                          # Do not attempt to put a comma here!
    Then                                                                        # Smallest so far
     {$S->copy($s);
      $F->copy($f);
     };
   });

  If $S > 0,                                                                    # The smallest set is not empty set so the intersection might not be empty either
  Then
   {$tree->findFirst;
    my $f = $tree->position($F);                                                # First tree (but the smallest sub tree would be better)

    $f->by(sub                                                                  # Insert each element of each sub tree
     {my ($t, undef, $nextElement) = @_;
      my $k = $t->key;

      $tree->by(sub                                                             # Each sub tree
       {my ($T, undef, $nextTree) = @_;
        If $F == $T->data, Then {Jmp $nextTree};                                # Skip the first tree

        my $t = $tree->position($T->data);
        $t->find($k);
        If $t->found == 0, Then {Jmp $nextElement};                             # Not found in this sub tree so it cannot be part of the intersection
       });
      $i->put($k, $k);
     });
   };

  $i                                                                            # Intersection
 }

#D2 Key String Trees                                                            # A key string tree has strings for keys.

sub Nasm::X86::Tree::getKeyString($$$)                                          # Find a string in a string tree and return the associated data and find status in the data and found fields of the tree.
 {my ($tree, $address, $size) = @_;                                             # Tree descriptor, address of key, length of key
  @_ == 3 or confess "Three parameters";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # should be optimized for case where string is less than one zmm

    my $t = $$s{tree};
    my $a = $t->area;
    my $d = $$p{data};

    PushR my $address = r15, my $remainder = r14, my $offset = r13, my $key = 31;
    $$p{address}->setReg($address);                                             # Address parameters
    $$p{size}   ->setReg($remainder);

    my $T = $t->cloneDescriptor;                                                # Copy the descriptor for the tree so we can reposition it if needed

    Block
     {my ($end) = @_;
      Mov $offset, 0;
      ForIn                                                                     # Find initial full blocks
       {Vmovdqu64 zmm($key), "[$address+$offset]";
        $T->find(zmm $key);
        If $T->found > 0,
        Then
         {$T->first->copy($T->data);                                            # The next sub tree down is addressed by the B<data> field of the tree descriptor
         },
        Else                                                                    # Not found at this level
         {$t->found->copy(0);                                                   # Show not found
          Jmp $end;                                                             # Finished
         };
       }
      Then                                                                      # Last block (which might be empty
       {Mov rsi, 0;
        Bts rsi, $remainder;                                                    # Set bit
        Dec rsi;                                                                # All the zeroes left of the bit are now ones
        Kmovq k1, rsi;
        Vmovdqu8 zmmMZ($key, 1), "[$address+$offset]";
        $T->find(zmm $key);                                                     # Find the zmm
       }, $offset, $remainder, RegisterSize(zmm0);

      $t->found->copy($T->found);                                               # Copy results into original tree
      $t->data ->copy($T->data);
     };

    PopR;
   } structures => {tree => $tree},
     parameters => [qw(address size)],
     name       =>  qq(Nasm::X86::Tree::getKeyString);

  $s->call(parameters => {address => $address, size=>$size},
           structures => {tree    => $tree});
 }

sub Nasm::X86::Tree::putKeyString($$$$)                                         # Associate a string of any length with a double word.
 {my ($tree, $address, $size, $data) = @_;                                      # Tree descriptor, address of key, length of key, data associated with key
  @_ == 4 or confess "Four parameters";

  $tree->stringTree or confess "Not a string tree";                             # Check that we are creating a string tree

  my $s = Subroutine                                                            # should be optimized for case where string is less than one zmm
   {my ($p, $s, $sub) = @_;

    my $t = $$s{tree}->cloneDescriptor;                                         # Clone the input tree descriptor so we can reposition it to handle strings longer than one zmm block
    my $a = $t->area;
    my $d = $$p{data};

    PushR my $area = r15, my $remainder = r14, my $offset = r13, my $zKey = 31;
    $$p{address}->setReg($area);                                                # Address parameters
    $$p{size}   ->setReg($remainder);

    Mov $offset, 0;
    ForIn                                                                       # Load initial full blocks into area
     {Vmovdqu64 zmm($zKey), "[$area+$offset]";
      $t->find(zmm $zKey);
      If $t->found > 0,
      Then
       {$t->first->copy($t->data);
       },
      Else
       {my $k = $a->appendZmm(zmm $zKey);
        my $T = $a->CreateTree(stringTree => 1);
        $t->put($k, $T);
        $t->copyDescriptor($T);
       };
     }
    Then                                                                        # Append remainder of string as a full zmm block padded with zeroes
     {Mov rsi, 0;
      Bts rsi, $remainder;                                                      # Set bit
      Dec rsi;                                                                  # All the zeroes left of the bit are now ones
      Kmovq k1, rsi;
      Vmovdqu8 "zmm1{k1}{z}", "[$area+$offset]";
      my $k = $a->appendZmm(1);                                                 # Place the zmm data into the area
      $t->put($k, $d);
     }, $offset, $remainder, RegisterSize(zmm0);

    PopR;
   } structures => {tree => $tree},
     parameters => [qw(address size data)],
     name       =>  qq(Nasm::X86::Tree::putKeyString);

  $tree->getKeyString($address, $size);                                         # See if we can find the string in the tree as an existing tree

  If $tree->found == 0,                                                         # Not found so insert
  Then
   {$s->call(parameters => {address => $address, size=>$size, data=>$data},
            structures => {tree    => $tree});
    $tree->found->copy(0);                                                      # We had to insert
   };
 }

sub Nasm::X86::Tree::uniqueKeyString($$$)                                       # Add a key string to a string tree if the key is not already present and return a unique number identifying the string (although currently there is no way to fast way to recover the string from the number). If the key string is already present in the string tree return the number associated with the original key string rather than creating a new entry.
 {my ($tree, $address, $size) = @_;                                             # Tree descriptor, address of key, length of key
  @_ == 3 or confess "Three parameters";

  PushR my $area = r15, my $first = r14, my $count = r13;
  $tree->area->address->setReg($area);
  $tree->first->setReg($first);
  my $o = $tree->stringTreeCountOff;                                            # Offset of count field - long keys are stored in multiple sub trees so the conventional size field is not enough
  Mov dWordRegister($count),"[$area+$first+$o]";                                # Number of strings so far

  my $c = V count => $count;
  $tree->putKeyString($address, $size, $c);                                     # Try to add the string
  If $tree->found == 0,
  Then                                                                          # A new key string was inserted
   {$c->getReg($count);
    Inc $count;
    $tree->area->address->setReg($area);                                        # The area might have moved due to the new allocation
    Mov "[$area+$first+$o]", dWordRegister($count);                             # Update string count after successful insert
   },
   Else
    {$c->copy($tree->data);
    };
   PopR;

  $c                                                                            # Unique string number as a variable
 }

#D2 Print                                                                       # Print a tree

sub Nasm::X86::Tree::dumpWithWidth($$$$$$$)                                     #P Dump a tree and all its sub trees.
 {my ($tree, $title, $width, $margin, $first, $keyX, $dataX) = @_;              # Tree, title, width of offset field, the maximum width of the indented area, whether to print the offset of the tree, whether to print the key field in hex or decimal, whether to print the data field in hex or decimal
  @_ == 7 or confess "Seven parameters";

  PushR my $F = 31;

  my $s = Subroutine                                                            # Print a tree
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition
    my $t = $$s{tree};                                                          # Tree
    my $I = $$p{indentation};                                                   # Indentation to apply to the start of each new line

    PushR my $transfer = r8, my $treeBitsR = r9, my $treeBitsIndexR = r10,
          my $K = 30, my $D = 29, my $N = 28;

    Block                                                                       # Print each node in the tree
     {my ($end, $start) = @_;                                                   # Labels
      my $offset = $$p{offset};                                                 # Offset of node to print
      $t->getBlock($offset, $K, $D, $N);                                        # Load node
      $t->getTreeBits($K, $treeBitsR);                                          # Tree bits for this node
      my $l = $t->lengthFromKeys($K);                                           # Number of nodes

      my $root = $t->rootFromFirst($F);                                         # Root or not

      $I->outSpaces;
      PrintOutString "At: ";                                                    # Print position and length
      $offset->outRightInHex(K width => $width);
      (K(col => $margin) - $I)->outSpaces;
      PrintOutString "length: ";
      $l->outRightInDec($width);

      PrintOutString ",  data: ";                                               # Position of data block
      $t->getLoop($K)->outRightInHex(K width => $width);

      PrintOutString ",  nodes: ";                                              # Position of nodes block
      $t->getLoop($D)->outRightInHex(K width => $width);

      PrintOutString ",  first: ";                                              # First block of tree
      $t->getLoop($N)->outRightInHex(K width => $width);

      my $U = $t->upFromData($D);                                               # Up field determines root / parent / leaf

      If $root == $offset,
      Then
       {PrintOutString ", root";                                                # Root
       },
      Else
       {PrintOutString ",  up: ";                                               # Up
        $U->outRightInHex(K width => $width);
       };

      If dFromZ($N, 0) == 0,                                                    # Leaf or parent
      Then
       {PrintOutString ", leaf";
       },
      Else
       {PrintOutString ", parent";
       };

      $t->getTreeBits($K, $transfer);
      Cmp $transfer, 0;
      IfGt
      Then                                                                      # Identify the data elements which are sub trees
       {PrintOutString ",  trees: ";
        V(bits => $transfer)->outRightInBin(K width => $t->maxKeys);
       };
      PrintOutNL;

      $I->copy($I + 2);                                                         # Indent sub tree

      $I->outSpaces; PrintOutString "Index:";                                   # Indices
      $l->for(sub
       {my ($index, $start, $next, $end) = @_;
        PrintOutString ' ';
        $index->outRightInDec($width);
       });
      PrintOutNL;

      my $printKD = sub                                                         # Print keys or data or nodes
       {my ($name, $zmm, $nodes, $tb) = @_;                                     # Key or data or node, zmm containing key or data or node, print nodes if true, print tree bits if true
        $I->outSpaces; PrintOutString $name;                                    # Keys
        Mov $treeBitsIndexR, 1 if $tb;                                          # Check each tree bit position

        ($nodes ? $l + 1 : $l)->for(sub                                         # There is one more node than keys or data
         {my ($index, $start, $next, $end) = @_;
          my $i = $index * $t->width;                                           # Key or Data offset
          my $k = dFromZ $zmm, $i;                                              # Key or Data

          if (!$tb)                                                             # No tree bits
           {PrintOutString ' ';
            $k->outRightInHex(K width => $width);
           }
          else
           {Test $treeBitsR, $treeBitsIndexR;                                   # Check for a tree bit
            IfNz
            Then                                                                # This key indexes a sub tree
             {if ($first)                                                       # Print out the offset of the first block as used by the sub tree
               {($k >> K(four => 4))->outRightInHex(K width => $width);         # This field indicates the offset of the first block
               }
              else                                                              # This key indexes a sub tree and for a reason which I have no desire to call to mind, I once thought it necessary to print the offset of the first node rather than the first block.
               {PushR 31;
                $t->area->getZmmBlock($k, 31);
                my $r = $t->rootFromFirst($F) >> K(four => 4);
                PopR;
                $r->outRightInHex(K width => $width);
               }
              PrintOutString '*';
             },
            Else
             {PrintOutString ' ';
              if ($name =~ m(key))
               {$k->outRightInHex($width) if     $keyX;
                $k->outRightInDec($width) unless $keyX;
               }
              else
               {$k->outRightInHex($width) if     $dataX;
                $k->outRightInDec($width) unless $dataX;
               }
             };
           }
          Shl $treeBitsIndexR, 1 if $tb;                                        # Next tree bit position
         });
        PrintOutNL;
       };

      $printKD->('Keys :', $K, 0, 0);                                           # Print key
      $printKD->('Data :', $D, 0, 1);                                           # Print data
      If dFromZ($N, 0) > 0,                                                     # If the first node is not zero we are not on a leaf
      Then
       {$printKD->('Nodes:', $N, 1, 0);
       };

      Cmp $treeBitsR, 0;                                                        # Any tree bits set?
      IfNe
      Then                                                                      # Tree bits present
       {Mov $treeBitsIndexR, 1;                                                 # Check each tree bit position
        PushR my $F = 31;                                                       # Load first block of sub tree
        K(loop => $t->maxKeys)->for(sub
         {my ($index, $start, $next, $end) = @_;
          Test $treeBitsR, $treeBitsIndexR;                                     # Check for a tree bit
          IfNz
          Then                                                                  # This key indexes a sub tree
           {my $i = $index * $t->width;                                         # Key/data offset
            my $d = dFromZ($D, $i);                                             # Data
            my $I = V(indentation => 0)->copy($I + 2 + 1);                      # Indent by one extra space to show separate sub tree rather than continuation of the existing tree and to make the at address line up with the address in data.

            my      $T = $t->position($d);
                    $T->firstFromMemory($F);                                    # First block for tree
            my $r = $T->rootFromFirst($F);

            if ($first)                                                         # The offset of the tree if requested
             {($I-2)->outSpaces;                                                # The word 'tree' is two letters longer than the word 'at'
              PrintOutString "Tree: ";
              $T->first->outRightInHexNL(K width => $width);
             }

            If $r == 0,                                                         # Empty tree
            Then
             {PrintOutStringNL "- empty";
             },
            Else
             {$sub->call(parameters => {indentation => $I, offset => $r},
                         structures => {tree        => $T});                    # Print sub tree referenced by data field
             };
           };
          Shl $treeBitsIndexR, 1;                                               # Next tree bit position
         });
        PopR;
       };

      If $l > 0,
      Then                                                                      # If the block only has one node it must be a leaf
       {($l+1)->for(sub                                                         # Print sub nodes
         {my ($index, $start, $next, $end) = @_;
          my $i = $index * $t->width;                                           # Key/Data offset
          my $d = dFromZ($N, $i);                                               # Sub nodes
          If $d > 0,                                                            # Print any sub nodes
          Then
           {my $I = V(indentation => 0)->copy($I + 2);
            $sub->call(parameters => {indentation => $I, offset=>$d},
                       structures => {tree        => $t});                      # Print sub tree referenced by data field
           };
         });
       };

      ($I - 2)->outSpaces; PrintOutStringNL "end";                              # Separate sub tree dumps

     };

    PopR;
   } parameters => [qw(indentation offset)],
     structures => {tree => $tree},
     name => "Nasm::X86::Tree::dump-$$tree{length}-$width-$margin-$first";

  PrintOutStringNL $title;                                                      # Title of the piece so we do not lose it

  $tree->firstFromMemory($F);                                                   # First block for tree
  my $Q = $tree->rootFromFirst($F);

  If $Q == 0,                                                                   # Empty tree
  Then
   {PrintOutStringNL "- empty";
   },
  Else
   {$tree->first->outNL("Tree: ") if $first;
    $s->call(structures => {tree        => $tree},                              # Print root node
             parameters => {indentation => V(indentation => 0),
                            offset      => $Q});
   };

  PopR;
 }

sub Nasm::X86::Tree::dump($$)                                                   #P Dump a tree and all its sub trees.
 {my ($tree, $title) = @_;                                                      # Tree, title
  @_ == 2 or confess "Two parameters";
  $tree->dumpWithWidth($title, 4, 20, 0, 1, 0)
 }

sub Nasm::X86::Tree::dump8($$)                                                  #P Dump a tree and all its sub trees using 8 character fields for numbers.
 {my ($tree, $title) = @_;                                                      # Tree, title
  @_ == 2 or confess "Two parameters";
  $tree->dumpWithWidth($title, 8, 80, 1, 1, 0)
 }

sub Nasm::X86::Tree::dump8xx($$)                                                #P Dump a tree and all its sub trees using 8 character fields for numbers printing the keys and data in hexadecimal.
 {my ($tree, $title) = @_;                                                      # Tree, title
  @_ == 2 or confess "Two parameters";
  $tree->dumpWithWidth($title, 8, 80, 1, 1, 1)
 }

sub Nasm::X86::Tree::printInOrder($$)                                           # Print a tree in order.
 {my ($tree, $title) = @_;                                                      # Tree, title
  @_ == 2 or confess "Two parameters";

  PushR my $F = 31;

  my $s = Subroutine                                                            # Print a tree
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $t = $$s{tree};                                                          # Tree
    my $area = $t->area;                                                        # Area

    PushR my $treeBitsR = r8, my $treeBitsIndexR = r9,
          my $K = 30, my $D = 29, my $N = 28;

    Block                                                                       # Print each node in the tree
     {my ($end, $start) = @_;                                                   # Labels
      my $offset = $$p{offset};                                                 # Offset of node to print
      $t->getBlock($offset, $K, $D, $N);                                        # Load node
      my $l = $t->lengthFromKeys($K);                                           # Number of nodes
      $l->for(sub                                                               # Print sub nodes
       {my ($index, $start, $next, $end) = @_;
        my $i = $index * $t->width;                                             # Key/Data?node offset
        my $k = dFromZ $K, $i;                                                  # Key
        my $d = dFromZ $D, $i;                                                  # Data
        my $n = dFromZ $N, $i;                                                  # Sub nodes
        If $n > 0,                                                              # Not a leaf
        Then
         {$sub->call(parameters => {offset => $n},                              # Recurse
                     structures => {tree   => $t});
         };
        $k->outRightInHex(K width => 4);                                        # Print key
       });

      If $l > 0,                                                                # Print final sub tree
      Then
       {my $o = $l * $t->width;                                                 # Final sub tree offset
        my $n = dFromZ $N, $l * $t->width;                                      # Final sub tree
        If $n > 0,                                                              # Not a leaf
        Then
         {$sub->call(parameters => {offset => $n},
                     structures => {tree   => $t});

         };
       };
     };
    PopR;
   } parameters => [qw(offset)],
     structures => {tree => $tree},
     name       => qq(Nasm::X86::Tree::printInOrder-$$tree{length}-$$tree{stringTree});

  PrintOutString $title;                                                        # Title of the piece so we do not lose it

  $tree->firstFromMemory($F);
  my $R = $tree->rootFromFirst($F);
  my $C = $tree->sizeFromFirst($F);

  If $R == 0,                                                                   # Empty tree
  Then
   {PrintOutStringNL "- empty";
   },
  Else
   {$C->outRightInDec(4);
    PrintOutString ": ";

     $s->call(structures => {tree  => $tree},                                   # Print root node
             parameters => {offset => $R});
    PrintOutNL;
   };

  PopR;
 }

sub Nasm::X86::Tree::outAsUtf8($)                                               # Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8.
 {my ($string) = @_;                                                            # Tree descriptor of string
  @_ == 1 or confess "One parameter";

  $string->by(sub                                                               # Each character
   {my ($i, $start, $next, $end) = @_;
    PushR rax;
    $string->data->setReg(rax);
    convert_rax_from_utf32_to_utf8;
    PrintOutRaxAsText;
    PopR;
   });
  $string                                                                       # Chain from the target string
 }

sub Nasm::X86::Tree::outAsUtf8NL($)                                             # Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8. Follow the print with a new line character.
 {my ($string) = @_;                                                            # Tree descriptor of string
  @_ == 1 or confess "One parameter";
  $string->outAsUtf8;
  PrintOutNL;
  $string                                                                       # Chain from the target string
 }

#if (1)                                                                          # Define operator overloading for trees
# {package Nasm::X86::Tree;
#  use overload
##   '+'  => \&add,
##   '-'  => \&sub,
##   '*'  => \&times,
##   '/'  => \&divide,
##   '%'  => \&mod,
##  '=='  => \&eq,
##  '!='  => \&ne,
##  '>='  => \&ge,
##   '>'  => \&gt,
##  '<='  => \&le,
##  '<'   => \&lt,
##  '++'  => \&inc,
#   '--'  => \&dec,
##  '""'  => \&str,
##  '&'   => \&and,                                                              # We use the zero flag as the bit returned by a Boolean operation so we cannot implement '&' or '|' which were previously in use because '&&' and '||' and "and" and "or" are all disallowed in Perl operator overloading.
##  '|'   => \&or,
#   '+='  => \&plusAssign,
##  '-='  => \&minusAssign,
##  '='   => \&equals,
##  '<<'  => \&shiftLeft,
##  '>>'  => \&shiftRight,
##  '!'    => \&not,
#   "fallback" => 1,
# }
#
#sub Nasm::X86::Tree::plusAssign($$)                                             # Use plus to push an element to a tree being used as a stack.
# {my ($tree, $data) = @_;                                                       # Tree being used as a stack, data to push
#
#  ref($data) =~ m(Variable|Tree) or                                             # Check right operand on right
#    confess "Need a tree or variable on the right";
#
#  $tree->push($data);                                                           # Perform the push
#  $tree                                                                         # The resulting tree
# }
#
#sub Nasm::X86::Tree::dec($)                                                     # Pop from the tree if it is being used as a stack.
# {my ($tree) = @_;                                                              # Tree being used as a stack
#
#  $tree->pop                                                                    # Perform the pop
# }

#D1 Unisyn                                                                      # Parse Unisyn language statements.

sub compactRangeIntoHex(@)                                                      #P Compact a range of numbers into hexadecimal.
 {my (@P) = @_;                                                                 # Numbers to compact
  my @p = sort @P;

  confess "Unsorted range" unless dump(@p) eq dump(@P);
  my @r;

  my sub format(@)                                                              # Format a range
   {my (@f) = @_;
    return push @r, sprintf "0x%x..0x%x", $f[0], $f[-1] if @f  > 1;
    return push @r, sprintf "0x%x",       $f[0]         if @f == 1;
    confess "Empty range";
   }

  my @f = shift @p;
  for(;@p;)                                                                     # Break into ranges
   {if ($f[-1] + 1 == $p[0])
     {push @f, shift @p;
     }
    else
     {format @f;
      @f = shift @p;
     }
   }
  format @f if @f;                                                              # Last range if any

  join ', ', @r;                                                                # Range as a string
 }

#D2 Lex                                                                         # Lexical Analysis

sub Nasm::X86::Unisyn::Lex::Number::S {0}                                       #P Start symbol.
sub Nasm::X86::Unisyn::Lex::Number::F {1}                                       #P End symbol.

sub Nasm::X86::Unisyn::Lex::Number::A {2}                                       #P ASCII characters extended with circled characters to act as escape sequences.
sub Nasm::X86::Unisyn::Lex::Letter::A {(0x0..0x7f, 0x24b6..0x24e9)}             #P

sub Nasm::X86::Unisyn::Lex::Number::p {3}                                       #P Prefix operator - applies only to the following variable or bracketed term.
sub Nasm::X86::Unisyn::Lex::Letter::p {(0x1d468...0x1d49b, 0x1d71c..0x1d755, map {ord} qw(₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₀))} #P

sub Nasm::X86::Unisyn::Lex::Number::v {4}                                       #P Variable names.
sub Nasm::X86::Unisyn::Lex::Letter::v {(0x1d5d4...0x1d607, 0x1d756..0x1d78f)}   #P

sub Nasm::X86::Unisyn::Lex::Number::q {5}                                       #P Suffix operator - applies only to the preceding variable or bracketed term.
sub Nasm::X86::Unisyn::Lex::Letter::q {(0x1d63c...0x1d66f,0x1d790..0x1d7c9, map {ord} qw(¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁰ ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ))}

sub Nasm::X86::Unisyn::Lex::Number::s {6}                                       #P Infix operator with left to right binding at priority 1.
sub Nasm::X86::Unisyn::Lex::Letter::s {(0x27e2)}

sub Nasm::X86::Unisyn::Lex::Number::b {7}                                       #P Open.
sub Nasm::X86::Unisyn::Lex::Letter::b                                           #P
 {(0x2308,0x230a,0x2329,0x2768,0x276a,0x276c,0x276e,0x2770,0x2772,0x2774,0x27e6,0x27e8,0x27ea,0x27ec,0x27ee,0x2983,0x2985,0x2987,0x2989,0x298b,0x298d,0x298f,0x2991,0x2993,0x2995,0x2997,0x29fc,0x2e28,0x3008,0x300a,0x3010,0x3014,0x3016,0x3018,0x301a,0xfd3e,0xff08,0xff5f)
 }

sub Nasm::X86::Unisyn::Lex::Number::B {8}                                       #P Close.
sub Nasm::X86::Unisyn::Lex::Letter::B                                           #P
 {(0x2309,0x230b,0x232a,0x2769,0x276b,0x276d,0x276f,0x2771,0x2773,0x2775,0x27e7,0x27e9,0x27eb,0x27ed,0x27ef,0x2984,0x2986,0x2988,0x298a,0x298c,0x298e,0x2990,0x2992,0x2994,0x2996,0x2998,0x29fd,0x2e29,0x3009,0x300b,0x3011,0x3015,0x3017,0x3019,0x301b,0xfd3f,0xff09,0xff60)
 }

sub Nasm::X86::Unisyn::Lex::Number::d{9}                                        #P
sub Nasm::X86::Unisyn::Lex::Letter::d                                           #P Dyad 2 - Double struck
 {(0x1d538..0x1d538+51, 0x2103, 0x210d, 0x2115, 0x2119, 0x211a, 0x211d,
   0x2124)
 }

sub Nasm::X86::Unisyn::Lex::Number::e{10}                                       #P
sub Nasm::X86::Unisyn::Lex::Letter::e                                           #P Dyad 3 - Mono
 {(0x1d670..0x1d670+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::a {11}                                      #P Assign infix operator with right to left binding at priority 4.
sub Nasm::X86::Unisyn::Lex::Letter::a                                           #P
 {(0x210e, 0x2190..0x21fe, 0xff1d, 0x1d434..0x1d454,
   0x1d456..0x1d467, 0x1d6e2..0x1d71b)
 }

sub Nasm::X86::Unisyn::Lex::Number::f {12}                                      #P
sub Nasm::X86::Unisyn::Lex::Letter::f                                           #P Dyad 5 - Sans-serif Normal
 {(0x1d5a0..0x1d5a0+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::g {13}                                      #P
sub Nasm::X86::Unisyn::Lex::Letter::g                                           #P Dyad 6 - Sans-serif Bold
 {(0x1d608..0x1d608+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::h {14}                                      #P
sub Nasm::X86::Unisyn::Lex::Letter::h                                           #P Dyad 7  - Calligraphy - normal
 {(0x1d49c..0x1d49c+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::i {15}                                      #P
sub Nasm::X86::Unisyn::Lex::Letter::i                                           #P Dyad 8 - Calligraphy - bold
 {(0x1d4d0..0x1d4d0+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::j {16}                                      #P
sub Nasm::X86::Unisyn::Lex::Letter::j                                           #P Dyad 9 - Fraktur - Normal
 {(0x1d504..0x1d504+51)
 }

sub Nasm::X86::Unisyn::Lex::Number::k{17}                                       #P
sub Nasm::X86::Unisyn::Lex::Letter::k                                           #P Dyad 10 - Fraktur - bold
 {(0x1d56c..0x1d56c+51)
 }
                                                                                #P
sub Nasm::X86::Unisyn::Lex::Number::l {18}                                      #P Dyad 11
sub Nasm::X86::Unisyn::Lex::Letter::l {(0x1d400..0x1d433,0x1d6a8..0x1d6e1)}

sub Nasm::X86::Unisyn::Lex::Number::m {19}                                      #P Dyad 12
sub Nasm::X86::Unisyn::Lex::Letter::m                                           #P
 {(0xac, 0xb1, 0xd7, 0xf7, 0x3f6, 0x606..0x608,
   0x200b..0x202E, 0x2030..0x2044,
   0x2047..0x205E, 0x2060..0x2061,
   0x2065..0x2069, 0x207a..0x207c, 0x208a..0x208c, 0x2118, 0x2140..0x2144,
   0x214b, 0x2200..0x2307, 0x230c..0x2328, 0x232c..0x23ff, 0x25a0..0x26ff,
   0x2715, 0x27c0..0x27e1, 0x27e3..0x27e5, 0x27f0..0x2982, 0x2999..0x29fb,
   0x29fe..0x2b58, 0x2e00..0x2e1f, 0x2e2a..0x2e30, 0xfb29, 0xfe62,
   0xfe64..0xfe66, 0xff0b, 0xff1c, 0xff1e, 0xff5c, 0xff5e, 0xffe2,
   0x1eef0..0x1eef1)
 }

sub Nasm::X86::Unisyn::Lex::Number::w {20}                                      #P White space
sub Nasm::X86::Unisyn::Lex::Letter::w                                           #P
 {(0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
   0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000)
 }

# Add: 1d5a0 ,  0x1d608,   0x1d49c,  0x1d4d0,  0x1d504,     0x1d56c,  0x1d670,  0x1d538

sub Nasm::X86::Unisyn::Lex::composeUnisyn($)                                    # Compose phrases of Unisyn and return them as a string
 {my ($words) = @_;                                                             # String of words
  my $s = '';

  my sub w($$)                                                                  # Variable name
   {my ($chars, $alpha) = @_;                                                   # Characters
    my @c = eval "Nasm::X86::Unisyn::Lex::Letter::$alpha";                      # Alphabet in array context

    for my $c(split //, $chars)
     {if ($alpha eq 'd')
       {$s .= '𝕒', next if  $c eq 'a';
        $s .= '𝕓', next if  $c eq 'b';
        $s .= '𝕔', next if  $c eq 'c';
        $s .= '𝕕', next if  $c eq 'd';
        $s .= '𝕖', next if  $c eq 'e';
        $s .= '𝕗', next if  $c eq 'f';
        $s .= '𝕘', next if  $c eq 'g';
        $s .= '𝕙', next if  $c eq 'h';
        $s .= '𝕚' , next if  $c eq 'i';
        $s .= '𝕛' , next if  $c eq 'j';
        $s .= '𝕜', next if  $c eq 'k';
        $s .= '𝕝',  next if  $c eq 'l';
        $s .= '𝕞', next if  $c eq 'm';
        $s .= '𝕟', next if  $c eq 'n';
        $s .= '𝕠', next if  $c eq 'o';
        $s .= '𝕡', next if  $c eq 'p';
        $s .= '𝕢', next if  $c eq 'q';
        $s .= '𝕣', next if  $c eq 'r';
        $s .= '𝕤', next if  $c eq 's';
        $s .= '𝕥', next if  $c eq 't';
        $s .= '𝕦', next if  $c eq 'u';
        $s .= '𝕧', next if  $c eq 'v';
        $s .= '𝕨', next if  $c eq 'w';
        $s .= '𝕩', next if  $c eq 'x';
        $s .= '𝕪', next if  $c eq 'y';
        $s .= '𝕫', next if  $c eq 'z';
        $s .= 'ℂ', next if  $c eq 'C';
        $s .= 'ℍ', next if  $c eq 'H';
        $s .= 'ℕ', next if  $c eq 'N';
        $s .= 'ℙ', next if  $c eq 'P';
        $s .= 'ℚ', next if  $c eq 'Q';
        $s .= 'ℤ', next if  $c eq 'Z';
       }
      if ($alpha eq 'd')
       {$s .= chr $c[ord($c) - ord('A')] if $c =~ m(\A[A-Z]\Z);
       }
      else
       {$s .= chr $c[ord($c) - ord('A')]      if $c =~ m(\A[A-Z]\Z);
        $s .= chr $c[ord($c) - ord('a') + 26] if $c =~ m(\A[a-z]\Z);
       }
     }
   }

  my sub c($$)                                                                  # Character from table
   {my ($pos, $alpha) = @_;                                                     # Position, character table name
    my @c = eval "Nasm::X86::Unisyn::Lex::Letter::$alpha";                      # Alphabet in array context
    chr $c[$pos]                                                                # Character requested
   }

  for my $w(split /\s+/, $words)
   {if ($w =~ m(\A[aAbBdefghijklmpqsSvw]))
     {if    ($w =~ m(\AA(.*)))  {$s .= $1}                                      # Ascii - normal letters where possible
      elsif ($w =~ m(\Aa=))     {$s .= "＝"}                                     # Assign chosen by number
      elsif ($w =~ m/\Ab\(/)    {$s .= '【'}                                     # Open bracket
      elsif ($w =~ m/\Ab\[/)    {$s .= '⟦'}                                     # Open bracket
      elsif ($w =~ m/\Ab\</)    {$s .= '⟨'}                                     # Open bracket
      elsif ($w =~ m(\Ab(\d+))) {$s .= c $1, "b"}                               # Open bracket
      elsif ($w =~ m/\AB\)/)    {$s .= '】'}                                     # Open bracket
      elsif ($w =~ m/\AB\]/)    {$s .= '⟧'}                                     # Open bracket
      elsif ($w =~ m/\AB\>/)    {$s .= '⟩'}                                     # Open bracket
      elsif ($w =~ m(\AB(\d+))) {$s .= c $1, "B"}                               # Close bracket
      elsif ($w =~ m(\Am\*))    {$s .= "✕"}                                     # Multiply
      elsif ($w =~ m(\Am\+))    {$s .= "＋"}                                     # Plus
      elsif ($w =~ m(\Ap(\d+))) {$s .= c $1, "p"}                               # Prefix chosen by number
      elsif ($w =~ m(\Aq(\d+))) {$s .= c $1, "q"}                               # Suffix chosen by number
      elsif ($w =~ m(\A[s;]\Z)) {$s .= c  0, "s"}                               # Semicolon
      elsif ($w =~ m(\AS\Z))    {$s .= ' '}                                     # Space
      elsif ($w =~ m(\Aw\Z))    {$s .= ' '}                                     # Single space
      elsif ($w =~ m(\Aw(\d+))) {$s .= ' ' x $1}                                # Spaces
      else
       {my $a = substr($w, 0, 1);
        if    ($w =~ m(\A$a(\d+))) {c $1, $a}                                   # Dyad chosen by number
        elsif ($w =~ m(\A$a(\w+))) {w $1, $a}                                   # Dyad chosen by letter
       }
     }
    else
     {confess "Invalid first character: $w";
     }
   }

  $s                                                                            # Composed string
 }

sub Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits {5}                 #P The number of bits needed to express a transition

sub Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray()                       # Create and load the table of lexical transitions.
 {my $a =  Nasm::X86::Unisyn::Lex::Number::a;                                   # Assign-2 - right to left
  my $A =  Nasm::X86::Unisyn::Lex::Number::A;                                   # Ascii
  my $b =  Nasm::X86::Unisyn::Lex::Number::b;                                   # Open
  my $B =  Nasm::X86::Unisyn::Lex::Number::B;                                   # Close
  my $d =  Nasm::X86::Unisyn::Lex::Number::d;                                   # Dyad
  my $F =  Nasm::X86::Unisyn::Lex::Number::F;                                   # Finish
  my $p =  Nasm::X86::Unisyn::Lex::Number::p;                                   # Prefix
  my $q =  Nasm::X86::Unisyn::Lex::Number::q;                                   # Suffix
  my $s =  Nasm::X86::Unisyn::Lex::Number::s;                                   # Semicolon-1
  my $S =  Nasm::X86::Unisyn::Lex::Number::S;                                   # Start
  my $v =  Nasm::X86::Unisyn::Lex::Number::v;                                   # Variable

  my @d = (Nasm::X86::Unisyn::Lex::Number::a,
           Nasm::X86::Unisyn::Lex::Number::e,
           Nasm::X86::Unisyn::Lex::Number::f,
           Nasm::X86::Unisyn::Lex::Number::g,
           Nasm::X86::Unisyn::Lex::Number::h,
           Nasm::X86::Unisyn::Lex::Number::i,
           Nasm::X86::Unisyn::Lex::Number::j,
           Nasm::X86::Unisyn::Lex::Number::k,
           Nasm::X86::Unisyn::Lex::Number::l,
           Nasm::X86::Unisyn::Lex::Number::m);

  my %x = (                                                                     # The transitions table: this tells us which combinations of lexical items are valid.  The table is augmented with start and end symbols so that we know where to start and end.
    $A => {map {$_=>1} $a,         $B, $d, $F,     $q, $s,   },
    $b => {map {$_=>1}     $A, $b, $B,         $p,     $s, $v},
    $B => {map {$_=>1} $a,         $B, $d, $F,     $q, $s    },
    $d => {map {$_=>1}     $A, $b,             $p,         $v},
    $p => {map {$_=>1}     $A, $b,                         $v},
    $q => {map {$_=>1} $a, $A,     $B, $d, $F,         $s    },
    $s => {map {$_=>1}     $A, $b, $B,     $F, $p,     $s, $v},
    $S => {map {$_=>1}     $A, $b,         $F, $p,     $s, $v},
    $v => {map {$_=>1} $a,         $B, $d, $F,     $q, $s    },
  );

  $x{$_} = $x{$d} for @d;                                                       # The infix operators all have the same sequencing

  for my $x(keys %x)
   {if ($x{$x}{$d})                                                             # If something can be followed by a dyad then it can be followed by any dyad
     {$x{$x}{$_} = 1 for @d;
     }
   }

  my @t;                                                                        # The transitions table will be held as an array of bytes

  for my $x(keys %x)                                                            # Each source lexical item
   {my %y = $x{$x}->%*;                                                         # The lexical items that can follow the current lexical item. If there are duplicates they are squashed out by assigning them to the same index in the target array
    for my $y(keys %y)                                                          # Each permissible transition
     {$t[($x << Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits) +      # Two dimensional index
          $y] = 1;
     }
   }

  $t[$_] //= -1 for 0..$#t;                                                     # Mark disallowed transitions

  (Rq(scalar @t), Rb(@t))                                                       # Return size of transitions array and array content
 }

sub Nasm::X86::Unisyn::Lex::AlphabetsArray                                      # Create an array of utf32 to alphabet number.
 {my %a =
   (Nasm::X86::Unisyn::Lex::Number::A   => [Nasm::X86::Unisyn::Lex::Letter::A],
    Nasm::X86::Unisyn::Lex::Number::p   => [Nasm::X86::Unisyn::Lex::Letter::p],
    Nasm::X86::Unisyn::Lex::Number::v   => [Nasm::X86::Unisyn::Lex::Letter::v],
    Nasm::X86::Unisyn::Lex::Number::q   => [Nasm::X86::Unisyn::Lex::Letter::q],
    Nasm::X86::Unisyn::Lex::Number::s   => [Nasm::X86::Unisyn::Lex::Letter::s],

    Nasm::X86::Unisyn::Lex::Number::b   => [Nasm::X86::Unisyn::Lex::Letter::b],
    Nasm::X86::Unisyn::Lex::Number::B   => [Nasm::X86::Unisyn::Lex::Letter::B],

    Nasm::X86::Unisyn::Lex::Number::d   => [Nasm::X86::Unisyn::Lex::Letter::d],
    Nasm::X86::Unisyn::Lex::Number::e   => [Nasm::X86::Unisyn::Lex::Letter::e],
    Nasm::X86::Unisyn::Lex::Number::a   => [Nasm::X86::Unisyn::Lex::Letter::a],
    Nasm::X86::Unisyn::Lex::Number::f   => [Nasm::X86::Unisyn::Lex::Letter::f],
    Nasm::X86::Unisyn::Lex::Number::g   => [Nasm::X86::Unisyn::Lex::Letter::g],
    Nasm::X86::Unisyn::Lex::Number::h   => [Nasm::X86::Unisyn::Lex::Letter::h],
    Nasm::X86::Unisyn::Lex::Number::i   => [Nasm::X86::Unisyn::Lex::Letter::i],
    Nasm::X86::Unisyn::Lex::Number::j   => [Nasm::X86::Unisyn::Lex::Letter::j],
    Nasm::X86::Unisyn::Lex::Number::k   => [Nasm::X86::Unisyn::Lex::Letter::k],
    Nasm::X86::Unisyn::Lex::Number::l   => [Nasm::X86::Unisyn::Lex::Letter::l],
    Nasm::X86::Unisyn::Lex::Number::m   => [Nasm::X86::Unisyn::Lex::Letter::m],
    Nasm::X86::Unisyn::Lex::Number::w   => [Nasm::X86::Unisyn::Lex::Letter::w],
   );

  my @a;
  for my $n(sort keys %a)
   {my @c = $a{$n}->@*;
    for my $c(@c)
     {$a[$c] = $n;
     }
   }

  $a[$_] //= -1 for 0..$#a;                                                     # Mark disallowed characters

  (Rq(scalar @a), Rb(@a))                                                       # Allowed utf32 characters array
 }

sub Nasm::X86::Unisyn::Lex::letterToNumber                                      # Map each letter in the union of the alphabets to a sequential number
 {my %a;                                                                        # Letters mapped to unique numbers
   for my $a(qw(A p v q s b B d e a f g h i j k l m w))
    {$a{$_} = keys %a for eval "Nasm::X86::Unisyn::Lex::Letter::$a";
    }

  my @a; $a[$_] = $a{$_} for sort keys %a;                                      # Mapping from letter to number

  $a[$_] //= -1 for 0..$#a;                                                     # Mark disallowed characters

  (K(size => scalar @a), K array => Rd @a)                                      # Size of array, array
 }

sub Nasm::X86::Unisyn::Lex::numberToLetter                                      # Recover a letter from its unique number
 {my %a;                                                                        # Letters mapped to unique numbers
  my $i = 0;
  for my $a(qw(A p v q s b B d e a f g h i j k l m w))
    {for my $a(eval "Nasm::X86::Unisyn::Lex::Letter::$a")                       # Each letter
      {$a{$a} = $i++;
       confess "Key mismatch on: ", sprintf("%x", $a), " at $i"
         unless keys(%a) == $i;                                                 # Check for duplicates
      }
    }

  my @a; $a[$a{$_}] = $_ for sort keys %a;                                      # Mapping from number to letter

  (K(size => scalar @a), K array => Rd @a)                                      # Size of array, array
 }

#D2 Parse                                                                       # Parse Unisyn language statements.

sub Nasm::X86::Unisyn::Lex::Reason::Success           {0};                      #P Successful parse.
sub Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};                      #P Bad utf8 character encountered.
sub Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};                      #P Character not part of Earl Zero.
sub Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};                      #P Transition from one lexical item to another not allowed.
sub Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};                      #P Trailing closing bracket discovered.
sub Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};                      #P Mismatched bracket.
sub Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};                      #P Expected something after final character.
sub Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};                      #P Open brackets not closed at end of.

sub Nasm::X86::Unisyn::Lex::position {0};                                       #P Position of the parsed item in the input text.
sub Nasm::X86::Unisyn::Lex::length   {1};                                       #P Length of the lexical item in bytes.
sub Nasm::X86::Unisyn::Lex::type     {2};                                       #P Type of the lexical item.
sub Nasm::X86::Unisyn::Lex::left     {3};                                       #P Left operand.
sub Nasm::X86::Unisyn::Lex::right    {4};                                       #P Right operand.
sub Nasm::X86::Unisyn::Lex::symbol   {5};                                       #P Symbol.

sub Nasm::X86::Unisyn::DescribeParse()                                          #P Describe a parse - create a description on the stack to receive the results of a parse
 {genHash("Nasm::X86::Unisyn::Parse",                                           # Parse results
    area     => DescribeArea,                                                   # The area in which the parse tree was built
    tree     => V('tree       '),                                               # The offset of the start of the parse tree in the parse area
    char     => V('parseChar  '),                                               # Last character processed
    fail     => V('parseFail  '),                                               # If not zero the parse has failed for some reason
    position => V('position   '),                                               # The position reached in the input string
    match    => V('parseMatch '),                                               # The position of the matching bracket  that did not match
    reason   => V('parseReason'),                                               # The reason code describing the failure if any
    symbols  => DescribeTree,                                                   # The symbol tree produced by the parse
    length   => V('s8         '),                                               # The length of the source
    source   => V('a8         '),                                               # The source text address
   );
 }

sub ParseUnisyn($$)                                                             # Parse a string of utf8 characters.
 {my ($a8, $s8) = @_;                                                           # Address of utf8 source string, size of utf8 source string in bytes

  my $p = Nasm::X86::Unisyn::DescribeParse;                                     # Describe a parse - create a description of the parse on the stack
     $p->source->copy($a8);                                                     # Source
     $p->length->copy($s8);                                                     # Length

  my $s = Subroutine                                                            # Print a tree
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    &Nasm::X86::Unisyn::Parse($$s{parse});

   } structures => {parse => $p}, name => qq(Nasm::X86::Unisyn::Parse);


  $s->call(structures=>{parse => $p});

  $p
 }

sub sortHashKeysByIntegerValues($)                                              #P Sort the keys of a hash whose values are integers by those values returning the keys so sorted as an array.
 {my ($h) = @_;                                                                 # Hash
  sort {$$h{$a} <=> $$h{$b}} sort keys %$h;                                     # Done here to avoid collisions with the sort special variables,
 }

sub Nasm::X86::Unisyn::Parse($)                                                 #P Parse a string of utf8 characters.
 {my ($pd) = @_;                                                                # Parse descriptor
  @_ == 1 or confess "One parameter";

  my $a8 = $pd->source;                                                         # Source to parse
  my $s8 = $pd->length;                                                         # Length of source in bytes

  my $parse       = CreateArea;                                                 # The area in which the parse tree will be be built

  my $brackets    = CreateArea(stack=>1);                                       # Brackets stack of zmm
  my $stack       = CreateArea(stack=>1);                                       # Parse tree stack of double words
  my $symbols     = $parse->CreateTree(stringTree=>1);                          # String tree of symbols encountered during the parse - they are stored along with the parse tree which refers to it.

  my $position    = V 'pos        ' => 0;                                       # Position in input string
  my $last        = V 'last       ' => Nasm::X86::Unisyn::Lex::Number::S;       # Last lexical type starting on this start symbol

  my $parseFail   = V 'parseFail  ' =>  1;                                      # If not zero the parse has failed for some reason
  my $parseReason = V 'parseReason' =>  0;                                      # The reason code describing the failure if L<parseFail> is not zero - we could probably merge these two fields
  my $parseMatch  = V 'parseMatch ' =>  0;                                      # The position of the bracket we failed to match
  my $parseChar   = V 'parseChar  ' =>  0;                                      # The last character recognized held as utf32
  my $firstChar   = V 'firstChar';                                              # First character recognized in a lexical item held as utf32

  my $startPos    = V 'startPos   ' =>  0;                                      # Start position of the last lexical item
  my $lastNew     = V 'lastNew    ' => -1;                                      # Offset of the last lexical created in the parse stack

  my $itemLength  = V 'itemLength'  => 0;                                       # Length of lexical item in unicode characters

  my $dWidth      = RegisterSize eax;                                           # Size of a dword

  my ($alphabetN, $alphabetA) = Nasm::X86::Unisyn::Lex::AlphabetsArray;         # Mapping of characters to alphabets
  my ($letterToNumberN, $letterToNumberA) =                                     # Map utf32 to letter number because it is smaller than a dword and so can be conveniently used to designate the lexical number of single character items
                                         Nasm::X86::Unisyn::Lex::letterToNumber;

  my $dumpStack = sub                                                           # Dump the parse stack
   {my $i = V i => 0;                                                           # Position in stack
    PrintOutStringNL "Dump parse stack";

    $stack->stackVariableSize->for(sub                                          # Each item on stack
     {my ($i, $start, $next, $end) = @_;
      my $o = $stack->stackVariable($i);
      $parse->getZmmBlock($o, 4);
      my $t = dFromZ(4, $dWidth * Nasm::X86::Unisyn::Lex::type);
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::S), Then {PrintOutString "Start    "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::F), Then {PrintOutString "End      "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::A), Then {PrintOutString "ASCII    "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::l), Then {PrintOutString "Infix3   "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::p), Then {PrintOutString "Prefix   "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::a), Then {PrintOutString "Assign   "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::v), Then {PrintOutString "Variable "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::q), Then {PrintOutString "Suffix   "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::s), Then {PrintOutString "Seperator"};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::m), Then {PrintOutString "Infix4   "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::b), Then {PrintOutString "Open     "};
      If $t == K(t => Nasm::X86::Unisyn::Lex::Number::B), Then {PrintOutString "Close    "};
     });
   };

  my $updateLength = sub                                                        # Update the length of the previous lexical item
   {If $lastNew >= 0,
    Then                                                                        # Update the last lexical item if there was one
     {my $l = $position - $startPos;                                            # Length of previous item
      my $s = V 'lexicalItemNumber';

      If $itemLength == 1,
      Then                                                                      # Single letter item so we use -its position in the alphabets as its lexical number as this is much faster than looking it up in a tree
       {my ($N, $A) = Nasm::X86::Unisyn::Lex::letterToNumber;

        $A->setReg(rax);                                                        # Address array of single letters
        $firstChar->setReg(rsi);                                                # The first and only character if the lexical item
        Mov edx, "[rax+4*rsi]";                                                 # The smaller number representing the letter
        Neg rdx;                                                                # Count down from zero to differentiate from multi letter lexical items which count up from zero.
        $s->getReg(rdx);                                                        # Access routine via a single character operator name
       },
      Else                                                                      # The lexical item has multiple characters in it so we look them up in the conventional manner
       {$s->copy($symbols->uniqueKeyString($a8+$startPos, $l));                 # The symbol number of the previous lexical item
       };

      $parse->getZmmBlock($lastNew, 0);                                         # Reload the description of the last lexical item
      $s->dIntoZ(0, $dWidth * Nasm::X86::Unisyn::Lex::symbol);                  # Record lexical symbol number of previous item in its describing tree
      $l->dIntoZ(0, $dWidth * Nasm::X86::Unisyn::Lex::length);                  # Record length of previous item in its describing tree
      $parse->putZmmBlock($lastNew, 0);                                         # Save the lexical item back into memory
      $lastNew->copy(-1);                                                       # Finished with this symbol
     };
    $startPos->copy($position);                                                 # Start of next lexical item
   };

  my $new = sub                                                                 # Create a new lexical item
   {ClearRegisters zmm0;
    $last      ->dIntoZ(0, $dWidth * Nasm::X86::Unisyn::Lex::type);             # Last lexical item recognized
    $position  ->dIntoZ(0, $dWidth * Nasm::X86::Unisyn::Lex::position);         # Position of lexical item
    my $o = $parse->appendZmm(0);                                               # Save the lexical item in the parse tree
    $stack->push($o);                                                           # Save the offset of the latest lexical item on the stack
    $lastNew->copy($o);                                                         # Update last created lexical item
    $itemLength->copy(0);                                                       # Length of lexical item in unicode characters
    $firstChar->copy($parseChar);                                               # Copy the utf32 value of the current character as it is the first character in the lexical item
   };

  &$new for 1..3;                                                               # Initialize the parse tree with three start symbols to act as sentinels

  my $prev = sub                                                                # Lexical item and type of the previous item on the parse stack
   {my $o = $stack->peek(1);
    $parse->getZmmBlock($o, 1);                                                 # Reload the description of the last lexical item
    dFromZ(1, $dWidth * Nasm::X86::Unisyn::Lex::type);                          # Lexical type
   };

  my $prev2 = sub                                                               # Lexical item and type of the previous previous item on the parse stack
   {my $o = $stack->peek(2);
    $parse->getZmmBlock($o, 2);                                                 # Reload the description of the last lexical item
    dFromZ(2, $dWidth * Nasm::X86::Unisyn::Lex::type);                          # Lexical type
   };

  my $double = sub                                                              # Double reduction - the right most item is placed 'left' under the second right most item
   {my $r = $stack->pop;                                                        # Right
    my $l = $stack->peek(1);                                                    # Left
    $parse->getZmmBlock($l, 1);                                                 # Reload the description of the last lexical item
    $r->dIntoZ(1, $dWidth * Nasm::X86::Unisyn::Lex::left);                      # Address right from left via 'left' field
    $parse->putZmmBlock($l, 1);                                                 # Update parse tree in memory
   };

  my $triple = sub                                                              # Triple reduction
   {my $r = $stack->pop;                                                        # Right most
    my $o = $stack->pop;                                                        # Operator
    my $l = $stack->pop;                                                        # Left operand
    $parse->getZmmBlock($o, 1);                                                 # Reload the description of the last lexical item
    $l->dIntoZ(1, $dWidth * Nasm::X86::Unisyn::Lex::left);
    $r->dIntoZ(1, $dWidth * Nasm::X86::Unisyn::Lex::right);
    $parse->putZmmBlock($o, 1);                                                 # Update parse tree in memory
    $stack->push($o);
   };

  my $a = sub                                                                   # Dyad2 = right to left associative
   {#PrintErrStringNL "Type: a";
    my $q = &$prev2;                                                            # Second previous item
    ifOr
     [sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::l},                     # Dyad2 preceded by dyad3 or dyad4
      sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::m}],
    Then
     {&$triple;                                                                 # Reduce the stack before adding the assignment
     };
    &$new;                                                                      # Push dyad2
   };

  my $A = sub                                                                   # Ascii
   {#PrintErrStringNL "Type: A";
    my $p = &$prev;
    &$new;
    If $p == K(p => Nasm::X86::Unisyn::Lex::Number::p),
    Then                                                                        # Previous is a prefix operator so we can append from it immediately
     {&$double;
     };
   };

  my $b = sub                                                                   # Open bracket
   {#PrintErrStringNL "Type: b";
    &$new;                                                                      # Push open bracket
   };

  my $B = sub                                                                   # Close bracket
   {#PrintErrStringNL "Type: B";
    my $p = &$prev;
    If $p == K(p => Nasm::X86::Unisyn::Lex::Number::s),                         # Pointless statement separator
    Then
     {$stack->pop;
     };
#    Ef {$p == K(p => Nasm::X86::Unisyn::Lex::Number::b)}                        # Empty bracket pair
#    Then
#     {
#     };
    Block                                                                       # Non empty pair of brackets - a single intervening bracket represents a previously collapsed bracketed expression
     {my ($end, $start) = @_;
      my $p = &$prev2;
      If $p == K(p => Nasm::X86::Unisyn::Lex::Number::b),
      Then                                                                      # Single item in brackets
       {&$double;
       },
      Ef {$p != K(p => Nasm::X86::Unisyn::Lex::Number::S)}
      Then                                                                      # Triple reduce back to start
       {&$triple;
        Jmp $start;                                                             # Keep on reducing until we meet the matching opening bracket
       };
     };
    If &$prev2 == K(p => Nasm::X86::Unisyn::Lex::Number::p),                    # Prefix operator preceding brackets
    Then
     {&$double;
     };
   };

  my $d = sub {my $q = &$prev2; ifAnd [                                         # List all the operators that have higher priority than the operator mentioned
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::d},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $e = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::e},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $f = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::f},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $g = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::g},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $h = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::h},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $i = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::i},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $j = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::j},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $k = sub {my $q = &$prev2; ifAnd [
      sub {$q >= K p => Nasm::X86::Unisyn::Lex::Number::k},
      sub {$q <= K p => Nasm::X86::Unisyn::Lex::Number::m},
      ], Then {&$triple}; &$new};

  my $l = sub                                                                   # Dyad3. This infix operator and all infix operators of higher priority
   {#PrintErrStringNL "Type: l";
    my $q = &$prev2;                                                            # Second previous item
    ifOr
     [sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::l},                     # Dyad3 preceded by dyad3 or dyad4
      sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::m}],
    Then
     {&$triple;                                                                 # Reduce
     };
    &$new;                                                                      # Push dyad3
   };

  my $m = sub                                                                   # Dyad4
   {#PrintErrStringNL "Type: m";
    my $q = &$prev2;                                                            # Second previous item
    If $q == K(p => Nasm::X86::Unisyn::Lex::Number::m),
    Then                                                                        # Dyad4 preceded by dyad4
     {&$triple;                                                                 # Reduce
     };
    &$new;                                                                      # Push dyad4
   };

  my $F = sub                                                                   # Final: at this point there are no brackets left.
   {#PrintErrStringNL "Type: F";
    &$updateLength;                                                             # Update the length of the previous lexical item

    Block                                                                       # Reduce all the remaining items on the stack
     {my ($end, $start) = @_;
      If &$prev2 != K(p => Nasm::X86::Unisyn::Lex::Number::S),
      Then                                                                      # Not at the end yet and room for a triple reduction
       {&$triple;
        Jmp $start;
       };
#     If &$prev2 == K(p => Nasm::X86::Unisyn::Lex::Number::S),
#     Then                                                                      # Not at the end yet with room for a final double reduction
#      {PrintErrStringNL "FFFF111";
#       &$prev2;
#       $stack->stackVariableSize->d;
#       &$double;
#       $stack->stackVariableSize->d;
#      };
     };
    my $top = $stack->pop;                                                      # Top of the parse tree
    $stack->clear;
    $stack->push($top);                                                         # New top of the parse tree
   };

  my $p = sub                                                                   # Prefix
   {#PrintErrStringNL "Type: p";
    &$new;                                                                      # Push prefix
   };

  my $q = sub                                                                   # Suffix
   {#PrintErrStringNL "Type: q";
    my $v = $stack->pop;                                                        # Pop currently top item
    &$new;                                                                      # Push suffix operator
    $stack->push($v);                                                           # Restore current item to top
    &$double;                                                                   # Place top under previous item leaving previous item on top of the stack
   };

  my $s = sub                                                                   # Statement separator
   {#PrintErrStringNL "Type: s";
    Block
     {my ($end, $start) = @_;
      my $p = &$prev;                                                           # Previous item
      ifOr                                                                      # Separator preceded by open or start - do nothing
       [sub {$p == K p => Nasm::X86::Unisyn::Lex::Number::b},
        sub {$p == K p => Nasm::X86::Unisyn::Lex::Number::s},
        sub {$p == K p => Nasm::X86::Unisyn::Lex::Number::S}],
      Then                                                                      # Eliminate useless statement separator
       {Jmp $end;
       };
      my $q = &$prev2;                                                          # Second previous item
      If $q == K(p => Nasm::X86::Unisyn::Lex::Number::s),
      Then                                                                      # Second previous is a statement separator
       {&$triple;                                                               # Reduce
        Jmp $end;                                                               # No need to push as we already have a statement separator on top of the stack
       };
      ifOr
       [sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::b},                   # Statement separator preceded by singleton
        sub {$q == K p => Nasm::X86::Unisyn::Lex::Number::S}],
      Then
       {&$new;                                                                  # Push statement separator after singleton
        Jmp $end;
       };
      &$triple;                                                                 # An operator at a higher level than statement separator so we can reduce
      Jmp $start;                                                               # Keep on reducing until we meet the matching opening bracket or start
     }
   };

  my $v = sub                                                                   # Variable
   {#PrintErrStringNL "Type: v";
    my $p = &$prev;
    &$new;
    If $p == K(p => Nasm::X86::Unisyn::Lex::Number::p),
    Then                                                                        # Previous is a prefix operator
     {&$double;
     };
   };

  my $w = sub                                                                   # Whitespace - ignore it
   {#PrintErrStringNL "Type: w";
   };

  my $S = sub                                                                   # Start symbol
   {PrintErrTraceBack "Should not be able to reach type S";
   };

  PushR my $lexType = r15, my $prevLexType = r14;                               # Current lexical type current, previous lexical item

  Mov $prevLexType, Nasm::X86::Unisyn::Lex::Number::S;                          # Start symbol

  $s8->for(sub                                                                  # Process up to the maximum number of characters
   {my ($index, undef, undef, $end) = @_;
    my ($char, $size, $fail) = GetNextUtf8CharAsUtf32 $a8 + $position;          # Get the next UTF-8 encoded character from the addressed memory and return it as a UTF-32 char.

    if (0)                                                                      # Debug
     {#PrintOutStringNL "AAAA";
      #$index->outNL;
      #$char->outNL;
      PrintErrStringNL "AAAA";
      $index->d;
      $char->d;
      #$stack->dump("Stack");
      #$parse->dump("Parse", 20);
     };

    $parseChar->copy($char);                                                    # Copy the current character

    If $fail > 0,
    Then                                                                        # Failed to convert a utf8 character
     {$parseReason  ->copy(Nasm::X86::Unisyn::Lex::Reason::BadUtf8);
      Jmp $end;
     };

    $itemLength++;                                                              # Increment unicode length

    $char->setReg(rsi);                                                         # Character

    Cmp rsi, "[$alphabetN]";                                                    # Compare with alphabet array limit

    IfGe
    Then                                                                        # Utf32 character is out of range of the alphabets array
     {Mov $lexType, -1;                                                         # Show invalid character
     },
    Else                                                                        # Character is in range
     {Mov rdx, $alphabetA;                                                      # Classify character into an alphabet
      Add rdx, $char->addressExpr;                                              # Position in alphabets array
      Mov byteRegister rdx, "[rdx]";                                            # Classify letter
      And rdx, 0xFF;                                                            # Clear rest of register

      Cmp rdx, Nasm::X86::Unisyn::Lex::Number::w;                               # Check for white space
      Cmovne $lexType, rdx;                                                     # Update lexical type unless it is white space which is being ignored
     };

    Cmp $lexType, -1;                                                           # Check found - performance is not at issue because we terminate the parse if the character has not been found
    IfEq
    Then                                                                        # Failed to classify character
     {$parseReason->copy(Nasm::X86::Unisyn::Lex::Reason::InvalidChar);
      Jmp $end;
     };

    my $change = V change => 0;                                                 # Changing from one lexical item to the next

    Block                                                                       # Classify character
     {my ($endClassify) = @_;                                                   # Code with labels supplied

      Cmp $lexType, Nasm::X86::Unisyn::Lex::Number::b;
      IfEq
      Then                                                                      # Opening bracket
       {ClearRegisters zmm1;
        $position      ->qIntoZ(1,    0);                                       # Details of opening bracket
        $char          ->qIntoZ(1,    8);                                       # Open
       ($char+1)       ->qIntoZ(1, 0x10);                                       # Close
        $brackets->pushZmm(zmm1);                                               # Save bracket description on bracket stack
        $change->copy(1);                                                       # Changing because we are on a bracket
        Jmp $endClassify;
       };

      Cmp $lexType, Nasm::X86::Unisyn::Lex::Number::B;
      IfEq
      Then                                                                      # Closing bracket
       {If $brackets->stackSize > 0,                                            # Something to match with on the brackets stack
        Then
         {$brackets->popZmm(1);                                                 # Details of matching bracket
          my $close = qFromZ(zmm1, 0x10);
          If $close != $char,                                                   # Closing bracket did not match the closing bracket derived from the opening bracket
          Then
           {my $position = dFromZ(zmm1, 0);
            $parseMatch ->copy($position);
            $parseReason->copy(Nasm::X86::Unisyn::Lex::Reason::Mismatch);       # Mismatched bracket
            Jmp $end;
           };
          $change->copy(1);                                                     # Changing because we are on a bracket
         },
        Else
         {$parseReason->copy(Nasm::X86::Unisyn::Lex::Reason::TrailingClose);
          Jmp $end;
         };
        Jmp $endClassify;
       };

      Cmp $lexType, $last->addressExpr;
      IfNe
      Then                                                                      # Change of current lexical item
       {$change->copy(1);                                                       # Changing because we are on a different lexical item
        Jmp $endClassify;
       };

      Cmp $lexType, Nasm::X86::Unisyn::Lex::Number::s;
      IfEq
      Then                                                                      # Statement separator is always one character wide as more would be pointless
       {$change->copy(1);                                                       # Changing because we are on a different lexical item
       };
     };

    If $change > 0,                                                             # Check the transition from the previous item
    Then                                                                        # Change of current lexical item
     {Cmp $lexType, Nasm::X86::Unisyn::Lex::Number::w;                          # Do not transition on white space

      IfNe
      Then                                                                      # Not white space
       {Mov rsi, $prevLexType;
        Shl rsi, Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits;
        Add rsi, $lexType;                                                      # The index into the allowed transitions array

        my ($tN, $tA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;    # Load permissible transitions
        Mov rax, $tA;
        Add rsi, rax;
        Mov al, "[rsi]";                                                        # Place transition symbol in low byte

        Cmp al, 0;                                                              # Test transition in low byte
        IfGt
        Then                                                                    # The transition on this lexical type was a valid transition
         {Mov $prevLexType, $lexType;                                           # New lexical type we will be transitioning on
          $last->copy($lexType);                                                # Treat unbroken sequences of a symbol as one lexical item
         },
        Else                                                                    # The transition on this lexical type was an invalid transition
         {$parseReason->copy(Nasm::X86::Unisyn::Lex::Reason::InvalidTransition);
          Jmp $end;
         };
       };

      &$updateLength;                                                           # Update the length of the previous lexical item

      Block                                                                     # Parse each lexical item to produce a parse tree of trees
       {my ($end, $start) = @_;                                                 # Code with labels supplied

        my %l = map {$_ => eval "Nasm::X86::Unisyn::Lex::Number::$_"}           # Lexical items to lexical item numbers
          qw(w a A b B l m p q s v d e f g h i j k S F);
        my @l = sortHashKeysByIntegerValues \%l;                                # Sort the lexical item types into numerical order
        my @c;                                                                  # Check numbers are unique and sequential
        my @L;                                                                  # Labels
        my $jumpTable = Label;                                                  # Start of jump table

        Comment "Jump to table";
        $last->setReg(rax);
        Shl rax, 4;
        Mov rsi, $jumpTable;
        Add rax, rsi;
        Jmp rax;

        for my $L(@l)                                                           # Create code to process each lexical item depending on its type
         {my $n = eval "Nasm::X86::Unisyn::Lex::Number::$L";
          $c[$n]++;
          push @L, SetLabel;
          my $c = "&\$$L";
          Comment "Lexical $L";
          eval $c;
          confess $@ if $@;
          Jmp $end;
         }

        confess "Lexical items do not start zero" unless defined $c[0];         # Check that the lexical item numbers can be used to create a jump table
        confess "Gaps in lexical items numbers"   unless @c == @l;
        for my $i(@c)
         {confess "Missing lexical item at $i"    unless ($c[$i]//0) == 1;      # Each jump has one lexical item
         }

        Comment "Jump Table target";
        Align 16;
        SetLabel $jumpTable;
        for my $L(@L)                                                           # The jump table
         {Jmp $L;                                                               # Jump to code that will process the lexical item
          Align 16;
         }

        Align 1024;
        PrintErrTraceBack "Unexpected lexical type" if $DebugMode;              # Something unexpected came along
       };
     };                                                                         # Else not required - we are continuing in the same lexical item

    $position->copy($position + $size);                                         # Point to next character to be parsed

    If $position >= $s8,
    Then                                                                        # We have reached the end of the input
     {Mov rsi, $prevLexType;                                                    # Lexical type to transition from
      Shl rsi, Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits;         # Two dimensional index
      Add rsi, Nasm::X86::Unisyn::Lex::Number::F;                               # Check we can transition to the final state from the current state

      my ($tN, $tA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;      # Load permissible transitions
      Mov rax, $tA;
      Add rsi, rax;
      Mov al, "[rsi]";

      Cmp al, 0;
      IfGt
      Then                                                                      # We are able to transition to the final state
       {If $brackets->stackSize == 0,                                           # Check that all the brackets have closed
        Then                                                                    # No outstanding brackets
         {&$F;
          $parseFail->copy(0);                                                  # Show success as a lack of failure
         },
        Else                                                                    # Open brackets not yet closed
         {$parseFail->copy(Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed);  # Error code
         };
       },
      Else                                                                      # We are not able to transition to the final state
       {$parseFail->copy(Nasm::X86::Unisyn::Lex::Reason::NotFinal);             # Error code
       };
      Jmp $end;                                                                 # Cannot parse further
     };
   });

  PopR;

  my $parseTree = $stack->pop; $stack->free; $brackets->free;                   # Obtain the parse tree (which is not a conventional tree) and free the brackets stack and parse stack

#$parse->dump("AA");

  $pd->area     ->copyDescriptor($parse);                                       # The area in which the parse tree was built
  $pd->tree     ->copy($parseTree);                                             # The offset of the start of the parse tree in the parse area - this is not a conventional Tree as defined elsewhere - it is specific to parsing.
  $pd->char     ->copy($parseChar);                                             # Last character processed
  $pd->fail     ->copy($parseFail);                                             # If not zero the parse has failed for some reason
  $pd->position ->copy($position);                                              # The position reached in the input string
  $pd->match    ->copy($parseMatch);                                            # The position of the matching bracket  that did not match
  $pd->reason   ->copy($parseReason);                                           # The reason code describing the failure if any
  $pd->symbols  ->copyDescriptor($symbols);                                     # The symbol tree produced by the parse

 } # Parse

sub Nasm::X86::Unisyn::Parse::dumpParseResult($)                                # Dump the result of a parse
 {my ($parse) = @_;                                                             # Parse
  @_ == 1 or confess "One parameter";

  $parse->char    ->outNL;                                                      # Print results
  $parse->fail    ->outNL;
  $parse->position->outNL;
  $parse->match   ->outNL;
  $parse->reason  ->outNL;

  $parse->dump;
 }

sub Nasm::X86::Unisyn::Parse::dump($)                                           #P Dump a parse tree in order.
 {my ($parse) = @_;                                                             # Parse
  @_ == 1 or confess "One parameter";

  my $w = RegisterSize(eax);                                                    # Width of an offset

#$parse->area->dump("PPPP", 20);

  my $s = Subroutine                                                            # Print a tree
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $area   = $$s{area};                                                     # Area
    my $source = $$p{source};                                                   # Source
    my $depth  = $$p{depth};                                                    # Depth
    my $offset = $$p{offset};                                                   # Offset of node of parse tree in containing area

    If $depth < K(key => 99),
    Then
     {$area->getZmmBlock($offset, 1);                                           # Load parse tree node

      my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of input
      my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in input
      my $type     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::type);              # Type of operator
      my $left     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::left);              # Left operand found
      my $right    = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::right);             # Right operand found

      If $length > 0,                                                           # Source text of lexical item
      Then
       {$depth->for(sub
         {PrintOutString "._";
         });

       ($source + $position)->printOutMemoryNL($length);                        # Write source text corresponding to the source lexical item
       };

      If $left > 0,
      Then                                                                      # There is a left sub tree
       {$sub->call(structures => {area => $area},
                   parameters => {depth => $depth+1, offset => $left, source => $source});
       };

      If $right > 0,
      Then                                                                      # There is a right sub tree
       {$sub->call(structures => {area => $area},
                   parameters => {depth => $depth+1, offset => $right, source => $source});
       };
     };
   } structures => {area => $parse->area}, parameters=>[qw(depth offset source)],
     name       => "Nasm::X86::Unisyn::Parse::dump";

  $s->call(structures => {area   => $parse->area},
           parameters => {depth  => K(depth => 0),
                          offset => $parse->tree,
                          source => $parse->source});
 }

sub Nasm::X86::Unisyn::Parse::dumpPostOrder($)                                  # Dump a parse tree in post order.
 {my ($parse) = @_;                                                             # Parse
  @_ == 1 or confess "One parameter";

  my $w = RegisterSize(eax);                                                    # Width of an offset

#$parse->area->dump("PPPP", 20);

  my $s = Subroutine                                                            # Print a tree
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine definition

    my $area   = $$s{area};                                                     # Area
    my $source = $$p{source};                                                   # Source
    my $depth  = $$p{depth};                                                    # Depth
    my $offset = $$p{offset};                                                   # Offset of node of parse tree in containing area

    If $depth < K(key => 99),
    Then
     {$area->getZmmBlock($offset, 1);                                           # Load parse tree node

      my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of input
      my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in input
      my $type     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::type);              # Type of operator
      my $left     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::left);              # Left operand found
      my $right    = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::right);             # Right operand found

      If $left > 0,
      Then                                                                      # There is a left sub tree
       {$sub->call(structures => {area => $area},
                   parameters => {depth => $depth+1, offset => $left, source => $source});
       };

      If $right > 0,
      Then                                                                      # There is a right sub tree
       {$sub->call(structures => {area => $area},
                   parameters => {depth => $depth+1, offset => $right, source => $source});
       };

      If $length > 0,                                                           # Source text of lexical item
      Then
       {$depth->for(sub
         {PrintOutString "._";
         });

       ($source + $position)->printOutMemoryNL($length);                        # Write source text corresponding to the source lexical item
       };
     };
   } structures => {area => $parse->area}, parameters=>[qw(depth offset source)],
     name       => "Nasm::X86::Unisyn::Parse::dump";

  $s->call(structures => {area   => $parse->area},
           parameters => {depth  => K(depth => 0),
                          offset => $parse->tree,
                          source => $parse->source});
 }

sub Nasm::X86::Unisyn::Parse::traverseApplyingLibraryOperators($$)              # Traverse a parse tree, in post order, applying a library of operators.
 {my ($parse, $library) = @_;                                                   # Parse tree, area containing a library
  @_ == 2 or confess "Two parameters";

  my ($intersection, $subroutines) =                                            # Create a tree mapping the subroutine numbers to subroutine offsets
    $library->readLibraryHeader
     ($parse->symbols, \&Nasm::X86::Unisyn::Lex::letterToNumber);               # Include single letter mapping

  my $s = Subroutine                                                            # Print a tree
   {my ($parameters, $structures, $subroutine) = @_;                            # Parameters, structures, subroutine definition
    my $parse        = $$structures{parse};                                     # Parse tree
    my $intersection = $$structures{intersection};                              # Intersection
    my $subroutines  = $$structures{subroutines};                               # Intersection
    my $library      = $$structures{library};                                   # Library
    my $offset       = $$parameters{offset};                                    # The offset in the containing area of the current node of the parse tree

    my $w = dSize;
    $parse->area->getZmmBlock($offset, 1);                                      # Load parse tree node
#    my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);             # Length of input
#    my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);           # Position in input
    my $type     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::type);                # Type of operator
    my $left     = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::left);                # Left operand found
    my $right    = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::right);               # Right operand found
    my $symbol   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::symbol);              # Lexical symbol

    If $left > 0,
    Then                                                                        # There is a left sub tree
     {$subroutine->call
       (structures => {parse        => $parse,
                       library      => $library,
                       intersection => $intersection,
                       subroutines  => $subroutines},
                       parameters   => {offset => $left});
     };

    If $right > 0,
    Then                                                                        # There is a right sub tree
     {$subroutine->call
       (structures => {parse        => $parse,
                       library      => $library,
                       intersection => $intersection,
                       subroutines  => $subroutines},
                       parameters   => {offset => $right});
     };

    my $sub = Subroutine {}                                                     # Subroutine definition used to call library subroutines
      structures => {parse => Nasm::X86::Unisyn::DescribeParse},
      parameters => [qw(offset)];

    my $byType = sub                                                            # Process a lexical item by just its type
     {my ($name) = @_;                                                          # Library subroutine name for this lexical type
      $subroutines->getKeyString(constantString($name));                        # Locate processing routine by name

      If $subroutines->found > 0,
      Then                                                                      # Located the subroutine for this lexical type
       {$sub->call(override => $library->address + $subroutines->data,          # Call the library routine
          parameters => {offset => $offset},
          structures => {parse  => $parse});
       };
     };

    my $byName = sub                                                            # Look up method name that matches the lexical item
     {$intersection->find($symbol);                                             # Lexical item number to library routine offset

      If $intersection->found > 0,
      Then
       {$sub->call(override => $library->address + $intersection->data,         # Call the library routine
          parameters => {offset => $offset},
          structures => {parse  => $parse});
       };
     };

    Block                                                                       # Process lexical item by type
     {my ($end) = @_;
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::A), Then {&$byType("Ascii");     Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::b), Then {&$byType("Open");      Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::B), Then {&$byType("Close");     Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::p), Then {&$byType("Prefix");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::q), Then {&$byType("Suffix");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::s), Then {&$byType("Separator"); Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::v), Then {&$byType("Variable");  Jmp $end};

      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::d), Then {&$byName("Dyad5 ");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::e), Then {&$byName("Dyad6 ");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::a), Then {&$byName("Assign");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::f), Then {&$byName("Dyad7 ");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::g), Then {&$byName("Dyad8 ");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::h), Then {&$byName("Dyad9 ");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::i), Then {&$byName("Dyad10");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::j), Then {&$byName("Dyad11");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::k), Then {&$byName("Dyad12");    Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::l), Then {&$byName("Dyad3");     Jmp $end};
      If $type == K(type => Nasm::X86::Unisyn::Lex::Number::m), Then {&$byName("Dyad4");     Jmp $end};
     };
   } structures => {parse       => $parse,
                   library      => $library,
                   intersection => $intersection,
                   subroutines  => $subroutines},
     parameters => [qw(offset)],
     name       => "Nasm::X86::Tree::traverseApplyingLibraryOperators";

  $s->call(structures => {parse        => $parse,                               # Call
                          library      => $library,
                          intersection => $intersection,
                          subroutines  => $subroutines},
           parameters=>  {offset       => $parse->tree});
 }

#D1 Assemble                                                                    # Assemble generated code

sub CallC($@)                                                                   # Call a C subroutine.
 {my ($sub, @parameters) = @_;                                                  # Name of the sub to call, parameters
  my
  @order = (rdi, rsi, rdx, rcx, r8, r9, r15);
  PushR @order;

  for my $i(keys @parameters)                                                   # Load parameters into designated registers
   {Mov $order[$i], $parameters[$i];
   }

  Push rax;                                                                     # Align stack on 16 bytes
  Mov rax, rsp;                                                                 # Move stack pointer
  Shl rax, 60;                                                                  # Get lowest nibble
  Shr rax, 60;
  IfEq                                                                          # If we are 16 byte aligned push two twos
  Then
   {Mov rax, 2; Push rax; Push rax;
   },
  Else                                                                          # If we are not 16 byte aligned push one one.
   {Mov rax, 1; Push rax;
   };

  if (ref($sub))                                                                # Where do we use this option?
   {Call $sub->start;
   }
  else                                                                          # Call named subroutine
   {Call $sub;
   }

  Pop r15;                                                                      # Decode and reset stack after 16 byte alignment
  Cmp r15, 2;                                                                   # Check for double push
  Pop r15;                                                                      # Single or double push
  IfEq Then {Pop r15};                                                          # Double push
  PopR @order;
 }

sub Extern(@)                                                                   # Name external references.
 {my (@externalReferences) = @_;                                                # External references
  push @extern, @_;
 }

sub Link(@)                                                                     # Libraries to link with.
 {my (@libraries) = @_;                                                         # Link library names which will be looked for on "LIBPATH"
  push @link, @_;
 }

my $lastAsmFinishTime;                                                          # The last time we finished an assembly

sub Start()                                                                     # Initialize the assembler.
 {@bss = @data = @rodata = %rodata = %rodatas = %subroutines = @text =
  @PushR = @extern = @link = @VariableStack = %loadAreaIntoAssembly = ();

  $DebugMode = 0;
  $Labels    = 0;
  $TraceMode = 0;
  SubroutineStartStack;                                                         # Number of variables at each lexical level
  $lastAsmFinishTime = time;                                                    # The last time we finished an assembly
 }

sub Exit(;$)                                                                    # Exit with the specified return code or zero if no return code supplied.  Assemble() automatically adds a call to Exit(0) if the last operation in the program is not a call to Exit.
 {my ($c) = @_;                                                                 # Return code
  $c //= 0;
  my $s = Subroutine
   {Comment "Exit code: $c";
    PushR rax, rdi;
    Mov rdi, $c;
    Mov rax, 60;
    Syscall;
    PopR;
   } name => "Exit_$c";

  $s->call;
 }

my $LocateIntelEmulator;                                                        # Location of Intel Software Development Emulator

sub LocateIntelEmulator()                                                       #P Locate the Intel Software Development Emulator.
 {my @locations = qw(/var/isde/sde64 sde/sde64 ./sde64);                        # Locations at which we might find the emulator
  my $downloads = q(/home/phil/Downloads);                                      # Downloads folder

  return $LocateIntelEmulator if defined $LocateIntelEmulator;                  # Location has already been discovered

  for my $l(@locations)                                                         # Try each locations
   {return $LocateIntelEmulator = $l if -e $l;                                  # Found it - cache and return
   }

  if (qx(sde64 -version) =~ m(Intel.R. Software Development Emulator))          # Try path
   {return $LocateIntelEmulator = "sde64";
   }

  return undef unless -e $downloads;                                            # Skip local install if not developing
  my $install = <<END =~ s(\n) (  && )gsr =~ s(&&\s*\Z) ()sr;                   # Install sde
cd $downloads
curl https://software.intel.com/content/dam/develop/external/us/en/documents/downloads/sde-external-8.63.0-2021-01-18-lin.tar.bz2 > sde.tar.bz2
tar -xf sde.tar.bz2
sudo mkdir -p /var/isde/
sudo cp -r * /var/isde/
ls -ls /var/isde/
END

  say STDERR qx($install);                                                      # Execute install

  for my $l(@locations)                                                         # Retry install locations after install
   {return $LocateIntelEmulator = $l if -e $l;                                  # Found it - cache and return
   }
  undef                                                                         # Still not found - give up
 }

sub getInstructionCount()                                                       #P Get the number of instructions executed from the emulator mix file.
 {return 0 unless -e $sdeMixOut;
  my $s = readFile $sdeMixOut;
  if ($s =~ m(\*total\s*(\d+))) {return $1}
  confess;
 }

sub OptimizePopPush(%)                                                          #P Perform code optimizations.
 {my (%options) = @_;                                                           # Options
  my %o = map {$_=>1} $options{optimize}->@*;
  if (1 or $o{if})                                                              # Optimize if statements by looking for the unnecessary reload of the just stored result
   {for my $i(1..@text-2)                                                       # Each line
     {my $t = $text[$i];
      if ($t =~ m(\A\s+push\s+(r\d+)\s*\Z)i)                                    # Push
       {my $R = $1;                                                             # Register being pushed
        my $s = $text[$i-1];                                                    # Previous line
        if ($s =~ m(\A\s+pop\s+$R\s*\Z)i)                                       # Matching push
         {my $r = $text[$i-2];
          if ($r =~ m(\A\s+mov\s+\[rbp-8\*\((\d+)\)],\s*$R\s*\Z)i)              # Save to variable
           {my $n = $1;                                                         # Variable number
            my $u = $text[$i+1];
            if ($u =~ m(\A\s+mov\s+$R,\s*\[rbp-8\*\($n\)]\s*\Z)i)               # Reload register
             {for my $j($i-1..$i+1)
               {$text[$j] = '; out '. $text[$j];
               }
             }
           }
         }
       }
     }
   }
 }

sub OptimizeReload(%)                                                           #P Reload: a = b; b = a;  remove second - as redundant.
 {my (%options) = @_;                                                           # Options
  my %o = map {$_=>1} $options{optimize}->@*;
  if (1 or $o{reload})
   {for my $i(1..@text-1)                                                       # Each line
     {my $a = $text[$i-1];
      my $b = $text[$i];
      if ($a =~ m(\Amov (\[.*?\]), ([^\[\]].*?)\Z))                             # Check a = b
       {my $a1 = $1; my $a2 = $2;
        if ($b eq qq(mov $a2, $a1\n))                                           # Check b = a
         {$text[$i] = q(; Reload removed: ).$text[$i];
         }
       }
     }
   }
 }

my $hasAvx512;

sub hasAvx512()                                                                 #P Check whether the current device has avx512 instructions or not.
 {return $hasAvx512 if defined $hasAvx512;
  $hasAvx512 = qx(cat /proc/cpuinfo | grep avx512) =~ m(\S) ? 1 : 0;            # Cache avx512 result
 }

sub lineNumbersToSubNamesFromSource                                             #P Create a hash mapping line numbers to subroutine definitions.
 {my @s = readFile $0;                                                          # Read source file
  my %l;                                                                        # Mapping from line number to current sub
  my $c;                                                                        # The current sub
  for my $i(keys @s)                                                            # Each line number
   {my $s = $s[$i];
    if ($s =~ m(\Asub ([^ \(]+)))
     {$c = $1 =~ s(\ANasm::X86::) ()r;                                          # Subroutine name  minus package name and parameters
     }
    if ($s =~ m(\A }))                                                          # End of sub
     {$c = undef;
     }
    $l{$i+1} = $c if $c;
   }
  %l
 }

sub locateRunTimeErrorInDebugTraceOutput($)                                     #P Locate the traceback of the last known good position in the trace file before the error occurred.
 {my ($trace) = @_;                                                             # Trace mode
  unlink $traceBack;                                                            # Traceback file
  return '' unless -e $sdeTraceOut;                                             # Need a trace file to get a traceback
  my @a = readFile $sdeTraceOut;                                                # Read trace file
  my $s = 0;                                                                    # Parse state
  my @p;                                                                        # Position in source file

  for my $a(reverse @a)                                                         # Read backwards
   {if (!$s)                                                                    # Looking for traceback start
     {if ($a =~ m(\AINS 0x[[:xdigit:]]{16}\s+MMX\s+movq\sr11,\s+mm0))
       {$s = 1;
       }
     }
    elsif ($s == 1)                                                             # In the traceback
     {if ($a =~ m(\AINS\s+0x[[:xdigit:]]{16}\s+BASE\s+mov r11, (0x[[:xdigit:]]+)))
       {unshift @p, eval $1;
        next;
       }
      last;                                                                     # Finished the scan of the traceback
     }
   }

  push my @t, "TraceBack start: ", "_"x80;                                      # Write the traceback
  my %l = lineNumbersToSubNamesFromSource();
  for my $i(keys @p)
   {my $p =  $p[$i];
    push @t, sprintf "%6d %s called at $0 line %d", $p, pad($l{$p}//'.', 32), $p;
   }
  push @t, "_" x 80;
  my $t = join "\n", @t;
  owf($traceBack, $t);                                                          # Place into a well known file
  say STDERR $t;
  $t
 }

sub fixMixOutput                                                                #P Fix mix output so we know where the code comes from in the source file.
 {return '' unless -e $sdeMixOut;                                               # Need a mix file to make this work
  my @a = readFile $sdeMixOut;                                                  # Read mix output
  my %l = lineNumbersToSubNamesFromSource();

  for my $i(keys @a)                                                            # Each line of output
   {if ($a[$i] =~ m(\AXDIS\s+[[:xdigit:]]{16}:\s+BASE\s+[[:xdigit:]]+\s+mov\s+r11,\s+(0x[[:xdigit:]]+)))
     {my $l = eval($1)+1;
      $a[$i] = sprintf "    %s called at $0 line %d\n", pad($l{$l}//'', 32), $l;
     }
   }
  my $a = join "", @a;                                                          # Updated mix out
  owf $sdeMixOut, join "", @a;                                                  # Save updated mix out
  $a
 }

sub countComments($)                                                            #P Count the number of comments in the text of the program so we can see what code is being generated too often.
 {my ($count) = @_;                                                             # Comment count
  if ($count)                                                                   # Count the comments so we can see what code to put into subroutines
   {my %c; my %b;                                                               # The number of lines between the comments, the number of blocks
    for my $c(readFile $sourceFile)
     {next unless $c =~ m(\A;);
      my @w = split /\s+/, $c, 3;
      $c{$w[1]}++;
     }

    my @c;
    for my $c(keys %c)                                                          # Remove comments that do not appear often
     {push @c, [$c{$c}, $b{$c}, $c] if $c{$c} >= $count;
     }
    my @d = sort {$$b[0] <=> $$a[0]} @c;
    say STDERR formatTable(\@d, [qw(Lines Blocks Comment)]) if @d;              # Print frequently appearing comments
   }
 }

sub numberWithUnderScores($)                                                    #P Place underscores in the string representation of a number.
 {my ($n) = @_;                                                                 # Number to add commas to
  scalar reverse join '_',  unpack("(A3)*", reverse $n);
 }

sub onGitHub                                                                    #P Whether we are on GitHub or not.
 {$ENV{GITHUB_REPOSITORY_OWNER}
 }

our $assembliesPerformed  = 0;                                                  # Number of assemblies performed
our $instructionsExecuted = 0;                                                  # Total number of instructions executed
our $totalBytesAssembled  = 0;                                                  # Total size of the output programs
our $testsThatPassed      = 0;                                                  # Number of runs that passed their test
our $testsThatFailed      = 0;                                                  # Number of runs that failed to pass their tests

sub Assemble(%)                                                                 # Assemble the generated code.
 {my (%options) = @_;                                                           # Options
  my $avx512     = delete $options{avx512};                                     # Avx512 instruction set needed
  my $clocks     = delete $options{clocks};                                     # Number of clocks required to execute this program - if a different number are required then a message is written to that effect.  Set mix > 0 for this to take effect.
  my $count      = delete $options{count}  // 0;                                # Count the comments that occur more frequently than this number
  my $debug      = delete $options{debug}  // 0;                                # Debug: 0 - print stderr and compare stdout to eq if present, 1 - print stdout and stderr and compare stderr to eq if present
  my $eq         = delete $options{eq};                                         # The expected output
  my $foot       = delete $options{foot};                                       # Foot print required
  my $keep       = delete $options{keep};                                       # Keep the executable rather than running it
  my $label      = delete $options{label};                                      # Label for this test if provided
  my $library    = delete $options{library};                                    # Create  the named library if supplied from the supplied assembler code
  my $list       = delete $options{list};                                       # Create and retain a listing file so we can see where a trace error occurs
  my $mix        = delete $options{mix} // ($clocks ? 1 : 0);                   # Create mix output and fix with line number locations in source - required if we want clocks
  my $ptr        = delete $options{ptr};                                        # Pointer check required
  my $trace      = delete $options{trace}  //0;                                 # Trace: 0 - none (minimal output), 1 - trace with sde64 and create a listing file to match
  confess "Invalid options: ".join(", ", sort keys %options) if keys %options;  # Complain about any invalid options

  my $execFile   = $keep // q(z);                                               # Executable file
  my $listFile   = q(z.txt);                                                    # Assembler listing
  my $objectFile = $library // q(z.o);                                          # Object file
  my $o1         = $programOut;                                                 # Stdout from run
  my $o2         = $programErr;                                                 # Stderr from run

  @PushR and confess "Mismatch PushR, PopR";                                    # Match PushR with PopR

  unlink $o1, $o2, $objectFile, $execFile, $listFile, $sourceFile;              # Remove output files

  Exit 0 unless $library or @text > 4 && $text[-4] =~ m(Exit code:);            # Exit with code 0 if an exit was not the last thing coded in a program but ignore for a library.

# Optimize(@_);                                                                 # Perform any optimizations requested
  OptimizeReload(@_);

  if (1)                                                                        # Concatenate source code
   {my $r = join "\n", map {s/\s+\Z//sr}   @rodata;
    my $d = join "\n", map {s/\s+\Z//sr}   @data;
    my $B = join "\n", map {s/\s+\Z//sr}   @bss;
    my $t = join "\n", map {s/\s+\Z//sr}   @text;
    my $x = join "\n", map {qq(extern $_)} @extern;
    my $N = $VariableStack[0];                                                  # Number of variables needed on the stack

    my $A = <<END;                                                              # Source code
bits 64
default rel
END

    $A .= <<END if $t and !$library;
global _start, main
  _start:
  main:
  Enter $N*8, 0
  $t
  Leave
END

    $A .= <<END if $t and $library;
  $t
END

    $A .= <<END if $r;
section .rodata
  $r
END
    $A .= <<END if $d;
section .data
  $d
END
    $A .= <<END if $B;
section .bss
  $B
  $d
END
    $A .= <<END if $x;
section .text
$x
END

    owf($sourceFile, $A);                                                       # Save source code to source file
   }

  if (!confirmHasCommandLineCommand(q(nasm)))                                   # Check for network assembler
   {my $f = fpf(currentDirectory, $sourceFile);
    say STDERR <<END;
Assember code written to the following file:

$f

I cannot compile this file because you do not have Nasm installed, see:

https://www.nasm.us/
END
    return;
   }

  my $emulate = hasAvx512 ? 0 : ($avx512 // 1) ;                                # Emulate if necessary
  my $sde     = LocateIntelEmulator;                                            # Locate the emulator
  my $run     = !$keep && !$library;                                            # Are we actually going to run the resulting code?

  if ($run and $emulate and !$sde)                                              # Complain about the emulator if we are going to run and we have not suppressed the emulator and the emulator is not present
   {my $f = fpf(currentDirectory, $execFile);
    say STDERR <<END;
Executable written to the following file:

$f

I am going to run this without using the Intel emulator. Your program will
crash if it contains instructions not implemented on your computer.

You can get the Intel emulator from:

https://software.intel.com/content/dam/develop/external/us/en/documents/downloads/sde-external-8.63.0-2021-01-18-lin.tar.bz2

To avoid this message, use option(1) below to produce just an executable
without running it, or use the option(2) to run without the emulator:

(1) Assemble(keep=>"executable file name")

(2) Assemble(avx512=>0)
END
    $emulate = 0;
   }

  if (my @emulatorFiles = searchDirectoryTreesForMatchingFiles(qw(. .txt)))     # Remove prior emulator output files
   {for my $f(@emulatorFiles)
     {unlink $f if $f =~ m(sde-mix-out);
     }
   }
  unlink $sdePtrCheck, $sdeMixOut, $sdeTraceOut, $traceBack;
  my $perlTime = 0; $perlTime = time - $lastAsmFinishTime if $lastAsmFinishTime;# Time we spent in Perl preparing for the assembly

  my $aStart = time;

  if (1)                                                                        # Assemble
   {my $I = @link ? $interpreter : '';                                          # Interpreter only required if calling C
    my $L = join " ",  map {qq(-l$_)} @link;                                    # List of libraries to link supplied via Link directive.
    my $e = $execFile;
    my $l = $trace || $list ? qq(-l $listFile) : q();                           # Create a list file if we are tracing because otherwise it it is difficult to know what we are tracing
    my $a = qq(nasm -O0 $l -o $objectFile $sourceFile);                         # Assembly options

    my $cmd  = $library
      ? qq($a -fbin)
      : qq($a -felf64 -g  && ld $I $L -o $e $objectFile && chmod 744 $e);

    qx($cmd);
    confess "Assembly failed $?" if $?;                                         # Stop if assembly failed
   }

  my $aTime = time - $aStart;

  countComments $count;                                                         # Count the number of comments

  my $out  = $run ? "1>$o1" : '';
  my $err  = $run ? "2>$o2" : '';

  my $exec = sub                                                                # Execution string
   {my $o = qq($sde);                                                           # Emulator
       $o = qq($o -ptr-check)      if $ptr;                                     # Emulator options - tracing
       $o = qq($o -footprint)      if $foot;                                    # Emulator options - foot print
       $o = qq($o -debugtrace)     if $trace;                                   # Emulator options - tracing
       $o = qq($o -mix)            if $mix;                                     # Emulator options - mix histogram output

    if ($emulate && !hasAvx512 or $trace or $mix or $ptr or $foot)              # Command to execute program via the  emulator
     {return qq($o -- ./$execFile $err $out)
     }

    qq(./$execFile $err $out);                                                  # Command to execute program without the emulator
   }->();

  my $eStart = time;

  #lll $exec;
  qx(timeout 30s $exec) if $run;                                                # Run unless suppressed by user or library

  my $er     = $?;                                                              # Execution result
  my $eTime  = time - $eStart;

  if ($run)                                                                     # Execution details
   {my $instructions       = getInstructionCount;                               # Instructions executed under emulator
    $instructionsExecuted += $instructions;                                     # Count instructions executed
    my $p = $assembliesPerformed++;                                             # Count assemblies

    my $bytes = (fileSize($execFile)//9448) - 9448;                             # Estimate the size of the output program
    $totalBytesAssembled += $bytes;                                             # Estimate total of all programs assembled

    my (undef, $file, $line) = caller();                                        # Line in caller

    say STDERR sprintf                                                          # Header if necessary
      ("# Test    %12s    %12s    %12s    %12s  %12s  %12s  %12s",
       "Clocks", "Bytes", "Total Clocks", "Total Bytes", "Run Time", "Assembler", "Perl")
      if $assembliesPerformed % 100 == 1;

    print STDERR                                                                # Rows
      sprintf("# %4s    %12s    %12s    %12s    %12s  %12.4f  %12.2f  %12.2f  at $file line $line",
      $label ? $label : sprintf("%4d", $assembliesPerformed),
      (map {numberWithUnderScores $_}
        $instructions, $bytes, $instructionsExecuted, $totalBytesAssembled),
        $eTime, $aTime, $perlTime);

    if (my $i = $instructions)                                                  # Clocks
     {if ($mix and my $c = $clocks)
       {if ($i != $c)
         {my $l = $c - $i;
          my $C = numberWithUnderScores $c;
          my $I = numberWithUnderScores $i;
          my $g = - $l;
          my $L = numberWithUnderScores $l;
          my $G = numberWithUnderScores $g;
          my $f = onGitHub ? "    " : "\n";
          print STDERR $f."Clocks were $C, but now $I, less $L"       if $l > 0;
          print STDERR $f."Clocks were $C, but now $I, greater by $G" if $g > 0;
         }
       }
     }
    print STDERR "\n";                                                          # Complete the execution detail line
   }

  if ($run and $debug == 0 and -e $o2 and my $s = readBinaryFile $o2)           # Print only errors if not debugging
   {print STDERR $s;
   }

  if ($run and $debug == 1)                                                     # Print files if soft debugging or error
   {print STDERR readFile($o1) if -e $o1;
    print STDERR readFile($o2) if -e $o2;
   }

  sub{my $a = fixMixOutput; say STDERR $a if $mix >= 2}->() if $run and $mix;   # Fix mix output to show where code came from in the source file

  if ($run and $trace)                                                          # Locate last execution point
   {locateRunTimeErrorInDebugTraceOutput($trace);
   }

  unlink $objectFile unless $library;                                           # Delete files
  unlink $execFile   unless $keep;                                              # Delete executable unless asked to keep it or its a library

  Start;                                                                        # Clear work areas for next assembly

  if ($run and defined(my $e = $eq))                                            # Diff results against expected
   {my $g = readFile($debug ? $o2 : $o1);
       $e =~ s(\s+#.*?\n) (\n)gs;                                               # Remove comments so we can annotate listings
    s(Subroutine trace back.*) ()s for $e, $g;                                  # Remove any trace back because the location of the subroutine in memory will vary
    if ($g ne $e)
     {my ($s, $G, $E) = stringsAreNotEqual($g, $e);
      if (length($s))
       {my $line = 1 + length($s =~ s([^\n])  ()gsr);
        my $char = 1 + length($s =~ s(\A.*\n) ()sr);
        say STDERR "Comparing wanted with got failed at line: $line, character: $char";
        say STDERR "Start:\n$s";
       }
      my $b1 = '+' x 80;
      my $b2 = '_' x 80;
      say STDERR "Want $b1\n", firstNChars($E, 80);
      say STDERR "Got  $b2\n", firstNChars($G, 80);
      say STDERR "Want: ", dump($e);
      say STDERR "Got : ", dump($g);

      if (0 and onGitHub)                                                       # Dump output files that might show why the failure occurred
       {for my $f($sdeMixOut, $sdePtrCheck, $sdeMixOut, $sdeTraceOut,
                  $o1, $o2, $traceBack)
         {if (-e $f)                                                            # Dump the file if it exists
           {say STDERR qx(ls -la $f; cat $f);
           }
         }
       }
      $testsThatFailed++;
      confess "Test failed" unless onGitHub;                                    # Test failed unless we are debugging test failures
     }
    else                                                                        # Runs that passed
     {$testsThatPassed++;
     }
    return 1;                                                                   # Test passed
   }

  return scalar(readFile($debug < 2 ? $o1 : $o2)) if $run;                      # Show stdout results unless stderr results requested
  $exec;                                                                        # Retained output
 }

sub totalBytesAssembled                                                         #P Total size in bytes of all files assembled during testing.
 {$totalBytesAssembled
 }

#d
#-------------------------------------------------------------------------------
# Export - eeee
#-------------------------------------------------------------------------------

if (0)                                                                          # Print exports
 {my %e =  map {$_=>1} @declarations;
  for my $a(readFile $0)
   {next unless $a =~ m(\Asub.*#);                                              # Must be a sub and not a sub forward declaration
    next if     $a =~ m(::);                                                    # Must be a high level sub
    next if     $a =~ m(#P);                                                    # Must not be private
    $a =~ m(\s+(.*?)\() ?  $e{$1}++ : 0;                                        # Save sub name
   }
  say STDERR q/@EXPORT_OK    = qw(/.join(' ', sort keys %e).q/);/;
  exit;
 }

use Exporter qw(import);

use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

@ISA          = qw(Exporter);
@EXPORT       = qw();
@EXPORT_OK    = qw(Add Align AllocateMemory And AndBlock Andn Assemble BinarySearchD Block Bsf Bsr Bswap Bt Btc Btr Bts Bzhi Call CallC   ChooseRegisters ClearMemory ClearRegisters ClearZF CloseFile Cmova Cmovae Cmovb Cmovbe Cmovc Cmove Cmovg Cmovge Cmovl Cmovle Cmovna Cmovnae Cmovnb Cmovne Cmp Comment  ConvertUtf8ToUtf32 CopyMemory CopyMemory4K CopyMemory64 Cpuid CreateArea Db Dd Dec DescribeArea Div Dq Ds Dw Ef Else Enter Exit Extern Fail For ForEver ForIn Fork FreeMemory GetNextUtf8CharAsUtf32 GetPPid GetPid GetPidInHex GetUid Hash Idiv If IfC IfEq IfGe IfGt IfLe IfLt IfNc IfNe IfNs IfNz IfS IfZ Imul Imul3 Inc Incbin Include InsertOneIntoRegisterAtPoint InsertZeroIntoRegisterAtPoint Ja Jae Jb Jbe Jc Jcxz Je Jecxz Jg Jge Jl Jle Jmp Jna Jnae Jnb Jnbe Jnc Jne Jng Jnge Jnl Jnle Jno Jnp Jns Jnz Jo Jp Jpe Jpo Jrcxz Js Jz K Kaddb Kaddd Kaddq Kaddw Kandb Kandd Kandnb Kandnd Kandnq Kandnw Kandq Kandw Kmovb Kmovd Kmovq Kmovw Knotb Knotd Knotq Knotw Korb Kord Korq Kortestb Kortestd Kortestq Kortestw Korw Kshiftlb Kshiftld Kshiftlq Kshiftlw Kshiftrb Kshiftrd Kshiftrq Kshiftrw Ktestb Ktestd Ktestq Ktestw Kunpckb Kunpckd Kunpckq Kunpckw Kxnorb Kxnord Kxnorq Kxnorw Kxorb Kxord Kxorq Kxorw Lahf Lea Leave Link LoadBitsIntoMaskRegister LoadConstantIntoMaskRegister LoadZmm Loop Lzcnt Mov Movd Movdqa Movq Movw Mulpd Neg Not OnSegv OpenRead OpenWrite Or OrBlock ParseUnisyn Pass Pdep Pext Pextrb Pextrd Pextrq Pextrw Pinsrb Pinsrd Pinsrq Pinsrw Pop PopR Popcnt Popfq PrintCString PrintCStringNL PrintErrOneRegisterInHex PrintErrOneRegisterInHexNL PrintErrRaxInHex PrintErrRaxInHexNL PrintErrRax_InHex PrintErrRax_InHexNL PrintErrRegisterInHex PrintErrRightInBin PrintErrRightInBinNL PrintErrRightInHex PrintErrRightInHexNL PrintErrTraceBack PrintMemory PrintMemoryInHex PrintMemory_InHex PrintNL PrintOneRegisterInHex PrintOutNL PrintOutOneRegisterInHex PrintOutOneRegisterInHexNL PrintOutRaxInHex PrintOutRaxInHexNL PrintOutRax_InHex PrintOutRax_InHexNL PrintOutRegisterInHex PrintOutRightInBin PrintOutRightInBinNL PrintOutRightInDec PrintOutRightInDecNL PrintOutRightInHex PrintOutRightInHexNL PrintOutSpace PrintOutString PrintOutStringNL PrintOutTraceBack PrintRaxAsChar PrintRaxAsText PrintRaxInDec PrintRaxInHex PrintRaxRightInDec PrintRax_InHex PrintRegisterInHex PrintRightInBin PrintRightInDec PrintRightInHex PrintSpace PrintString PrintStringNL PrintTraceBack Pslldq Psrldq Push Pushfq R RComment Rb Rd Rdtsc ReadArea ReadChar ReadFile ReadInteger ReadLine ReadTimeStampCounter RegisterSize RestoreFirstFour RestoreFirstFourExceptRax RestoreFirstSeven RestoreFirstSevenExceptRax Ret Rq Rs Rutf8 Rw Sal Sar SaveFirstFour SaveFirstSeven SaveRegIntoMm SetLabel SetMaskRegister SetZF Seta Setae Setb Setbe Setc Sete Setg Setge Setl Setle Setna Setnae Setnb Setnbe Setnc Setne Setng Setnge Setnl Setno Setnp Setns Setnz Seto Setp Setpe Setpo Sets Setz Shl Shr Start StatSize StringLength Sub Subroutine Syscall Test Then ToZero Tzcnt V Vaddd Vaddpd Valignb Valignd Valignq Valignw Variable Vcvtudq2pd Vcvtudq2ps Vcvtuqq2pd Vdpps Vgetmantps Vmovd Vmovdqa32 Vmovdqa64 Vmovdqu Vmovdqu32 Vmovdqu64 Vmovdqu8 Vmovq Vmulpd Vpaddb Vpaddd Vpaddq Vpaddw Vpandb Vpandd Vpandnb Vpandnd Vpandnq Vpandnw Vpandq Vpandw Vpbroadcastb Vpbroadcastd Vpbroadcastq Vpbroadcastw Vpcmpeqb Vpcmpeqd Vpcmpeqq Vpcmpeqw Vpcmpub Vpcmpud Vpcmpuq Vpcmpuw Vpcompressd Vpcompressq Vpexpandd Vpexpandq Vpextrb Vpextrd Vpextrq Vpextrw Vpgatherqd Vpgatherqq Vpinsrb Vpinsrd Vpinsrq Vpinsrw Vpmovb2m Vpmovd2m Vpmovm2b Vpmovm2d Vpmovm2q Vpmovm2w Vpmovq2m Vpmovw2m Vpmullb Vpmulld Vpmullq Vpmullw Vporb Vpord Vporq Vporvpcmpeqb Vporvpcmpeqd Vporvpcmpeqq Vporvpcmpeqw Vporw Vprolq Vpsubb Vpsubd Vpsubq Vpsubw Vptestb Vptestd Vptestq Vptestw Vpxorb Vpxord Vpxorq Vpxorw Vsqrtpd WaitPid Xchg Xor ah al ax bFromX bFromZ bRegFromZmm bRegIntoZmm bh bl bp bpl bx byteRegister ch checkZmmRegister cl constantString copyStructureMinusVariables countComments createBitNumberFromAlternatingPattern cs cx dFromPointInZ dFromX dFromZ dRegFromZmm dRegIntoZmm dWordRegister dh di dil dl ds dx eax ebp ebx ecx edi edx es esi esp executeFileViaBash extractRegisterNumberFromMM fs getBwdqFromMm gs ifAnd ifOr k0 k1 k2 k3 k4 k5 k6 k7 loadAreaIntoAssembly locateRunTimeErrorInDebugTraceOutput mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 opposingJump qFromX qFromZ r10 r10b r10d r10l r10w r11 r11b r11d r11l r11w r12 r12b r12d r12l r12w r13 r13b r13d r13l r13w r14 r14b r14d r14l r14w r15 r15b r15d r15l r15w r8 r8b r8d r8l r8w r9 r9b r9d r9l r9w rax rbp rbx rcx rdi rdx registerNameFromNumber rflags rip rsi rsp si sil sp spl ss st0 st1 st2 st3 st4 st5 st6 st7 unlinkFile uptoNTimes wFromX wFromZ wRegFromZmm wRegIntoZmm wordRegister xmm xmm0 xmm1 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm2 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm3 xmm30 xmm31 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 ymm ymm0 ymm1 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm2 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm3 ymm30 ymm31 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 zmm zmm0 zmm1 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm2 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm3 zmm30 zmm31 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmmM zmmMZ);
%EXPORT_TAGS  = (all=>[@EXPORT, @EXPORT_OK]);


sub extractDocumentationFlags {}

# podDocumentation
=pod

=encoding utf-8

=head1 Name

Nasm::X86 - Generate X86 assembler code using Perl as a macro pre-processor.

=head1 Synopsis

Write and execute B<x64> B<Avx512> assembler code from L<perl> using L<perl> as
a powerful macro assembler.  The generated code can be run under the Intel
emulator to obtain execution trace and instruction counts.

Please see: L<https://github.com/philiprbrenan/NasmX86> for a complete working
demonstration of how to run code produced by this module and foir examples of
its use.

While this module allows you to intermix Perl and Assembler code it is
noticeable that the more Perl code that is written the less new Assembler code
is required because there are more opportunities to call a Perl routine to
generate the required Assembler code rather than writing the Assembler out by
hand.

Use B<Avx512> instructions to perform B<64> comparisons in parallel.

  my $P = "2F";                                                                 # Value to test for
  my $l = Rb 0;  Rb $_ for 1..RegisterSize zmm0;                                # 0..63
  Vmovdqu8 zmm0, "[$l]";                                                        # Load data to test
  PrintOutRegisterInHex zmm0;

  Mov rax, "0x$P";                                                              # Broadcast the value to be tested
  Vpbroadcastb zmm1, rax;
  PrintOutRegisterInHex zmm1;

  for my $c(0..7)                                                               # Each possible test
   {my $m = "k$c";
    Vpcmpub $m, zmm1, zmm0, $c;
    PrintOutRegisterInHex $m;
   }

  Kmovq rax, k0;                                                                # Count the number of trailing zeros in k0
  Tzcnt rax, rax;
  PrintOutRegisterInHex rax;

  is_deeply Assemble, <<END;                                                    # Assemble and test
  zmm0: 3F3E 3D3C 3B3A 3938   3736 3534 3332 3130   2F2E 2D2C 2B2A 2928   2726 2524 2322 2120   1F1E 1D1C 1B1A 1918   1716 1514 1312 1110   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100
  zmm1: 2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F   2F2F 2F2F 2F2F 2F2F
    k0: 0000 8000 0000 0000
    k1: FFFF 0000 0000 0000
    k2: FFFF 8000 0000 0000
    k3: 0000 0000 0000 0000
    k4: FFFF 7FFF FFFF FFFF
    k5: 0000 FFFF FFFF FFFF
    k6: 0000 7FFF FFFF FFFF
    k7: FFFF FFFF FFFF FFFF
   rax: 0000 0000 0000 002F
END

With the print statements removed, the Intel Emulator indicates that 26
instructions were executed:

  CALL_NEAR                                                              1
  ENTER                                                                  2
  JMP                                                                    1
  KMOVQ                                                                  1
  MOV                                                                    5
  POP                                                                    1
  PUSH                                                                   3
  SYSCALL                                                                1
  TZCNT                                                                  1
  VMOVDQU8                                                               1
  VPBROADCASTB                                                           1
  VPCMPUB                                                                8

  *total                                                                26

=head1 Description

Generate X86 assembler code using Perl as a macro pre-processor.


Version "20220712".


The following sections describe the methods in each functional area of this
module.  For an alphabetic listing of all methods by name see L<Index|/Index>.



=head1 Labels

Create and set labels.

=head2 Label()

Create a unique label. Useful for constructing for and if statements.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;


    my $l = Label;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head2 SetLabel($l)

Create (if necessary) and set a label in the code section returning the label so set.

     Parameter  Description
  1  $l         Label

B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;

    SetLabel $l;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head1 Data

Layout data

=head2 Global variables

Create variables in the data segment if you are willing to make your program non reentrant.

=head3 Ds(@d)

Layout bytes in memory and return their label.

     Parameter  Description
  1  @d         Data to be laid out

B<Example:>


    my $q = Rs('a'..'z');

    Mov rax, Ds('0'x64);                                                          # Output area  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Vmovdqu32(xmm0, "[$q]");                                                      # Load
    Vprolq   (xmm0,   xmm0, 32);                                                  # Rotate double words in quad words
    Vmovdqu32("[rax]", xmm0);                                                     # Save
    Mov rdi, 16;
    PrintOutMemoryNL;

    ok Assemble eq=><<END;
  efghabcdmnopijkl
  END


=head3 Db(@bytes)

Layout bytes in the data segment and return their label.

     Parameter  Description
  1  @bytes     Bytes to layout

B<Example:>


    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Dw(@words)

Layout words in the data segment and return their label.

     Parameter  Description
  1  @words     Words to layout

B<Example:>


    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Dd(@dwords)

Layout double words in the data segment and return their label.

     Parameter  Description
  1  @dwords    Double words to layout

B<Example:>


    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Dq(@qwords)

Layout quad words in the data segment and return their label.

     Parameter  Description
  1  @qwords    Quad words to layout

B<Example:>


    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head2 Global constants

Create constants in read only memory,

=head3 Rb(@bytes)

Layout bytes in the data segment and return their label.

     Parameter  Description
  1  @bytes     Bytes to layout

B<Example:>



    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Rw(@words)

Layout words in the data segment and return their label.

     Parameter  Description
  1  @words     Words to layout

B<Example:>



    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Rd(@dwords)

Layout double words in the data segment and return their label.

     Parameter  Description
  1  @dwords    Double words to layout

B<Example:>



    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Rq(@qwords)

Layout quad words in the data segment and return their label.

     Parameter  Description
  1  @qwords    Quad words to layout

B<Example:>



    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;
    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END


=head3 Rs(@d)

Layout bytes in read only memory and return their label.

     Parameter  Description
  1  @d         Data to be laid out

B<Example:>


    Comment "Print a string from memory";
    my $s = "Hello World";

    Mov rax, Rs($s);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rdi, length $s;
    PrintOutMemory;
    Exit(0);

    ok Assemble(avx512=>0) =~ m(Hello World);


    my $q = Rs('abababab');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r10, 0x10;
    Mov r11, 0x11;
    Mov r12, 0x12;
    Mov r13, 0x13;
    Mov r14, 0x14;
    Mov r15, 0x15;
    Mov  r8, 0x08;
    Mov  r9, 0x09;
    Mov rax, 1;
    Mov rbx, 2;
    Mov rcx, 3;
    Mov rdi, 4;
    Mov rdx, 5;
    Mov rsi, 6;
    PrintOutRegistersInHex;

    my $r = Assemble avx512=>0, eq=><<END;
  rfl: .... .... .... .2.2
  r10: .... .... .... ..10
  r11: .... .... .... .2.6
  r12: .... .... .... ..12
  r13: .... .... .... ..13
  r14: .... .... .... ..14
  r15: .... .... .... ..15
   r8: .... .... .... ...8
   r9: .... .... .... ...9
  rax: .... .... .... ...1
  rbx: .... .... .... ...2
  rcx: .... .... ..40 197F
  rdi: .... .... .... ...4
  rdx: .... .... .... ...5
  rsi: .... .... .... ...6
  END


=head3 Rutf8(@d)

Layout a utf8 encoded string as bytes in read only memory and return their label.

     Parameter  Description
  1  @d         Data to be laid out

B<Example:>


    my ($out, $size, $fail);

    my $Chars = Rb(0x24, 0xc2, 0xa2, 0xc9, 0x91, 0xE2, 0x82, 0xAC, 0xF0, 0x90, 0x8D, 0x88);
    my $chars = V(chars => $Chars);

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+0;                        # Dollar               UTF-8 Encoding: 0x24                UTF-32 Encoding: 0x00000024
    $out->out('out1 : ');
    $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+1;                        # Cents                UTF-8 Encoding: 0xC2 0xA2           UTF-32 Encoding: 0x000000a2
    $out->out('out2 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+3;                        # Alpha                UTF-8 Encoding: 0xC9 0x91           UTF-32 Encoding: 0x00000251
    $out->out('out3 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+5;                        # Euro                 UTF-8 Encoding: 0xE2 0x82 0xAC      UTF-32 Encoding: 0x000020AC
    $out->out('out4 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+8;                        # Gothic Letter Hwair  UTF-8 Encoding  0xF0 0x90 0x8D 0x88 UTF-32 Encoding: 0x00010348
    $out->out('out5 : ');     $size->outNL(' size : ');

    my $statement = qq(𝖺
 𝑎𝑠𝑠𝑖𝑔𝑛 【【𝖻 𝐩𝐥𝐮𝐬 𝖼】】
AAAAAAAA);                        # A sample sentence to parse


    my $s = K(statement => Rutf8($statement));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $l = StringLength $s;

    my $address = AllocateMemory $l;                                              # Allocate enough memory for a copy of the string
    CopyMemory($s, $address, $l);

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address;
    $out->out('outA : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+4;
    $out->out('outB : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+5;
    $out->out('outC : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+30;
    $out->out('outD : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+35;
    $out->out('outE : ');     $size->outNL(' size : ');

    $address->printOutMemoryInHexNL($l);

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  out1 : .... .... .... ..24 size : .... .... .... ...1
  out2 : .... .... .... ..A2 size : .... .... .... ...2
  out3 : .... .... .... .251 size : .... .... .... ...2
  out4 : .... .... .... 20AC size : .... .... .... ...3
  out5 : .... .... ...1 .348 size : .... .... .... ...4
  outA : .... .... ...1 D5BA size : .... .... .... ...4
  outB : .... .... .... ...A size : .... .... .... ...1
  outC : .... .... .... ..20 size : .... .... .... ...1
  outD : .... .... .... ..20 size : .... .... .... ...1
  outE : .... .... .... ..10 size : .... .... .... ...2
  F09D 96BA .A20 F09D  918E F09D 91A0 F09D  91A0 F09D 9196 F09D  9194 F09D 919B 20E3  8090 E380 90F0 9D96  BB20 F09D 90A9 F09D  90A5 F09D 90AE F09D  90AC 20F0 9D96 BCE3  8091 E380 91.A 4141  4141 4141 4141 ....
  END


=head1 Registers

Operations on registers

=head2 Size

Sizes of each register

=head3 RegisterSize($R)

Return the size of a register.

     Parameter  Description
  1  $R         Register

B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END


    ok 8 == RegisterSize rax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



=head2 Push and Pop

Generic versions of push and pop with pop popping the last push.

=head3 PushR(@r)

Push registers onto the stack.

     Parameter  Description
  1  @r         Registers

B<Example:>


    Mov rax, 0x11111111;
    Mov rbx, 0x22222222;

    PushR my @save = (rax, rbx);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 0x33333333;
    PopR;
    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... 1111 1111
     rbx: .... .... 2222 2222
  END

    LoadZmm(17, 0x10..0x50);
    PrintOutRegisterInHex zmm17;
    Mov r14, 2; Mov r15, 3;
    PrintOutRegisterInHex r14, r15;

    PushR 14, 15, 16..31;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    LoadZmm(17, 0x20..0x70);
    PrintOutRegisterInHex zmm17;
    Mov r14, 22; Mov r15, 33;
    PopR;
    PrintOutRegisterInHex zmm17;
    PrintOutRegisterInHex r14, r15;
    ok Assemble eq => <<END, avx512=>1;
   zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
     r14: .... .... .... ...2
     r15: .... .... .... ...3
   zmm17: 5F5E 5D5C 5B5A 5958  5756 5554 5352 5150 - 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 + 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
   zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
     r14: .... .... .... ...2
     r15: .... .... .... ...3
  END


=head3 PopR(@r)

Pop registers from the stack. Use the last stored set if none explicitly supplied.  Pops are done in reverse order to match the original pushing order.

     Parameter  Description
  1  @r         Register

B<Example:>


    Mov rax, 0x11111111;
    Mov rbx, 0x22222222;
    PushR my @save = (rax, rbx);
    Mov rax, 0x33333333;

    PopR;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... 1111 1111
     rbx: .... .... 2222 2222
  END

    LoadZmm(17, 0x10..0x50);
    PrintOutRegisterInHex zmm17;
    Mov r14, 2; Mov r15, 3;
    PrintOutRegisterInHex r14, r15;
    PushR 14, 15, 16..31;
    LoadZmm(17, 0x20..0x70);
    PrintOutRegisterInHex zmm17;
    Mov r14, 22; Mov r15, 33;

    PopR;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex zmm17;
    PrintOutRegisterInHex r14, r15;
    ok Assemble eq => <<END, avx512=>1;
   zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
     r14: .... .... .... ...2
     r15: .... .... .... ...3
   zmm17: 5F5E 5D5C 5B5A 5958  5756 5554 5352 5150 - 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 + 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
   zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
     r14: .... .... .... ...2
     r15: .... .... .... ...3
  END


=head2 Save and Restore

Saving and restoring registers via the stack

=head3 SaveFirstFour(@keep)

Save the first 4 parameter registers making any parameter registers read only.

     Parameter  Description
  1  @keep      Registers to mark as read only

B<Example:>


    Mov rax, 1;
    Mov rdi, 1;

    SaveFirstFour;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;


    SaveFirstFour;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;


    SaveFirstFour;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 RestoreFirstFour()

Restore the first 4 parameter registers.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;

    RestoreFirstFour;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 RestoreFirstFourExceptRax()

Restore the first 4 parameter registers except rax so it can return its value.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;

    RestoreFirstFourExceptRax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 SaveFirstSeven()

Save the first 7 parameter registers.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;

    SaveFirstSeven;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;

    SaveFirstSeven;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;

    SaveFirstSeven;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 RestoreFirstSeven()

Restore the first 7 parameter registers.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    RestoreFirstSeven;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSevenExceptRax;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 RestoreFirstSevenExceptRax()

Restore the first 7 parameter registers except rax which is being used to return the result.


B<Example:>


    Mov rax, 1;
    Mov rdi, 1;
    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstSeven;
    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFour;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    RestoreFirstSevenExceptRax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax, rdi;
    RestoreFirstFourExceptRax;
    PrintOutRegisterInHex rax, rdi;

    SaveFirstFour;
    Mov rax, 2;
    Mov rdi, 2;
    SaveFirstSeven;
    Mov rax, 3;
    Mov rdi, 4;
    PrintOutRegisterInHex rax, rdi;

    Bswap rax;
    PrintOutRegisterInHex rax;

    my $l = Label;
    Jmp $l;
    SetLabel $l;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...2
     rdi: .... .... .... ...2
     rax: .... .... .... ...1
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .... .... .... ...3
     rdi: .... .... .... ...2
     rax: .... .... .... ...3
     rdi: .... .... .... ...1
     rax: .... .... .... ...3
     rdi: .... .... .... ...4
     rax: .3.. .... .... ....
  END

    ok 8 == RegisterSize rax;


=head3 ClearRegisters(@registers)

Clear registers by setting them to zero.

     Parameter   Description
  1  @registers  Registers

B<Example:>


    Mov rax,1;
    Kmovq k0,  rax;
    Kaddb k0,  k0, k0;
    Kaddb k0,  k0, k0;
    Kaddb k0,  k0, k0;
    Kmovq rax, k0;
    PushR k0;

    ClearRegisters k0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Kmovq k1, k0;
    PopR  k0;
    PrintOutRegisterInHex k0;
    PrintOutRegisterInHex k1;

    ok Assemble( eq => <<END)
      k0: .... .... .... ...8
      k1: .... .... .... ...0
  END


=head2 Zero flag

Actions on the Zero Flag.

=head3 SetZF()

Set the zero flag.


B<Example:>



    SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutZF;
    ClearZF;
    PrintOutZF;

    SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutZF;

    SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutZF;
    ClearZF;
    PrintOutZF;


    SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};
    ClearZF;
    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head3 ClearZF()

Clear the zero flag.


B<Example:>


    SetZF;
    PrintOutZF;

    ClearZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutZF;
    SetZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;

    ClearZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutZF;

    SetZF;
    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};

    ClearZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head2 x, y, zmm

Actions specific to mm registers

=head3 xmm(@r)

Add xmm to the front of a list of register expressions.

     Parameter  Description
  1  @r         Register numbers

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);

    PrintOutRegisterInHex xmm(1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head3 ymm(@r)

Add ymm to the front of a list of register expressions.

     Parameter  Description
  1  @r         Register numbers

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head3 zmm(@r)

Add zmm to the front of a list of register expressions.

     Parameter  Description
  1  @r         Register numbers

B<Example:>


    LoadZmm 0, 0..63;

    PrintOutRegisterInHex zmm 0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  END


=head3 zmmM($z, $m)

Add zmm to the front of a register number and a mask after it.

     Parameter  Description
  1  $z         Zmm number
  2  $m         Mask register

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;

    Vmovdqu8 zmmM (2, 7), zmm(1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head3 zmmMZ($z, $m)

Add zmm to the front of a register number and mask and zero after it.

     Parameter  Description
  1  $z         Zmm number
  2  $m         Mask register number

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);

    Vmovdqu8 zmmMZ(3, 7), zmm(1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head3 Via general purpose registers

Load zmm registers from data held in the general purpose registers.

=head4 LoadZmm($zmm, @bytes)

Load a numbered zmm with the specified bytes.

     Parameter  Description
  1  $zmm       Numbered zmm
  2  @bytes     Bytes

B<Example:>



    LoadZmm 0, 0..63;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex zmm 0;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  END


=head4 bRegFromZmm($register, $zmm, $offset)

Load the specified register from the byte at the specified offset located in the numbered zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);


    bRegFromZmm(r15, 1, 1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 bRegIntoZmm($register, $zmm, $offset)

Put the byte content of the specified register into the byte in the numbered zmm at the specified offset in the zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;

    bRegIntoZmm(r15, 1,  0);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    bRegIntoZmm(r15, 1,  1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 wRegFromZmm($register, $zmm, $offset)

Load the specified register from the word at the specified offset located in the numbered zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;


    wRegFromZmm(r14, 1, 3);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 wRegIntoZmm($register, $zmm, $offset)

Put the specified register into the word in the numbered zmm at the specified offset in the zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 dRegFromZmm($register, $zmm, $offset)

Load the specified register from the double word at the specified offset located in the numbered zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;


    dRegFromZmm(r14, 1, 3);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 dRegIntoZmm($register, $zmm, $offset)

Put the specified register into the double word in the numbered zmm at the specified offset in the zmm.

     Parameter  Description
  1  $register  Register to load
  2  $zmm       Numbered zmm register to load from
  3  $offset    Constant offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);

    dRegIntoZmm(r15, 1,  4);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    dRegIntoZmm(r15, 1,  8);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    dRegIntoZmm(r15, 1, 12);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 SaveRegIntoMm($mm, $offset, $reg)

Save the specified register into the numbered zmm at the quad offset specified as a constant number.

     Parameter  Description
  1  $mm        Mm register
  2  $offset    Offset in quads
  3  $reg       General purpose register to load

B<Example:>



    Mov rax, 1; SaveRegIntoMm(zmm0, 0, rax);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Mov rax, 2; SaveRegIntoMm(zmm0, 1, rax);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Mov rax, 3; SaveRegIntoMm(zmm0, 2, rax);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Mov rax, 4; SaveRegIntoMm(zmm0, 3, rax);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    LoadRegFromMm(zmm0, 0, r15);
    LoadRegFromMm(zmm0, 1, r14);
    LoadRegFromMm(zmm0, 2, r13);
    LoadRegFromMm(zmm0, 3, r12);

    PrintOutRegisterInHex ymm0, r15, r14, r13, r12;
    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    ymm0: .... .... .... ...4  .... .... .... ...3 - .... .... .... ...2  .... .... .... ...1
     r15: .... .... .... ...1
     r14: .... .... .... ...2
     r13: .... .... .... ...3
     r12: .... .... .... ...4
  END


=head3 Via variables

Load zmm registers from data held in variables

=head4 bFromX($xmm, $offset)

Get the byte from the numbered xmm register and return it in a variable.

     Parameter  Description
  1  $xmm       Numbered xmm
  2  $offset    Offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;


    bFromX(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;


    bFromX(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 wFromX($xmm, $offset)

Get the word from the numbered xmm register and return it in a variable.

     Parameter  Description
  1  $xmm       Numbered xmm
  2  $offset    Offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;


    wFromX(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 dFromX($xmm, $offset)

Get the double word from the numbered xmm register and return it in a variable.

     Parameter  Description
  1  $xmm       Numbered xmm
  2  $offset    Offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;


    dFromX(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 qFromX($xmm, $offset)

Get the quad word from the numbered xmm register and return it in a variable.

     Parameter  Description
  1  $xmm       Numbered xmm
  2  $offset    Offset in bytes

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;


    qFromX(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 bFromZ($zmm, $offset, %options)

Get the byte from the numbered zmm register and return it in a variable.

     Parameter  Description
  1  $zmm       Numbered zmm
  2  $offset    Offset in bytes
  3  %options   Options

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;

    bFromZ(1, 0)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;

    bFromZ(1, 1)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    bFromZ(1, 2)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 wFromZ($zmm, $offset, %options)

Get the word from the numbered zmm register and return it in a variable.

     Parameter  Description
  1  $zmm       Numbered zmm
  2  $offset    Offset in bytes
  3  %options   Options

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;

    wFromZ(1, 1)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    wFromZ(1, 2)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 dFromZ($zmm, $offset, %options)

Get the double word from the numbered zmm register and return it in a variable.

     Parameter  Description
  1  $zmm       Numbered zmm
  2  $offset    Offset in bytes
  3  %options   Options

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;

    dFromZ(1, 1)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    dFromZ(1, 2)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;

    dFromZ(1, 2)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head4 qFromZ($zmm, $offset, %options)

Get the quad word from the numbered zmm register and return it in a variable.

     Parameter  Description
  1  $zmm       Numbered zmm
  2  $offset    Offset in bytes
  3  %options   Options

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;

    qFromZ(1, 1)->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    dFromZ(1, 2)->outNL;

    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head2 Mask

Operations on mask registers

=head3 SetMaskRegister($mask, $start, $length)

Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.

     Parameter  Description
  1  $mask      Number of mask register to set
  2  $start     Register containing start position or 0 for position 0
  3  $length    Register containing end position

B<Example:>


    Mov rax, 8;
    Mov rsi, -1;

    Inc rsi; SetMaskRegister(0, rax, rsi); PrintOutRegisterInHex k0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(1, rax, rsi); PrintOutRegisterInHex k1;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(2, rax, rsi); PrintOutRegisterInHex k2;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(3, rax, rsi); PrintOutRegisterInHex k3;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(4, rax, rsi); PrintOutRegisterInHex k4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(5, rax, rsi); PrintOutRegisterInHex k5;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(6, rax, rsi); PrintOutRegisterInHex k6;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Inc rsi; SetMaskRegister(7, rax, rsi); PrintOutRegisterInHex k7;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
      k0: .... .... .... ...0
      k1: .... .... .... .1..
      k2: .... .... .... .3..
      k3: .... .... .... .7..
      k4: .... .... .... .F..
      k5: .... .... .... 1F..
      k6: .... .... .... 3F..
      k7: .... .... .... 7F..
  END


=head3 LoadBitsIntoMaskRegister($mask, $prefix, @values)

Load a bit string specification into a mask register in two clocks.

     Parameter  Description
  1  $mask      Number of mask register to load
  2  $prefix    Prefix bits
  3  @values    +n 1 bits -n 0 bits

B<Example:>


    for (0..7)
     {ClearRegisters "k$_";
      K($_,$_)->setMaskBit("k$_");
      PrintOutRegisterInHex "k$_";
     }

    ClearRegisters k7;

    LoadBitsIntoMaskRegister(7, '1010', -4, +4, -2, +2, -1, +1, -1, +1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex "k7";

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
      k0: .... .... .... ...1
      k1: .... .... .... ...2
      k2: .... .... .... ...4
      k3: .... .... .... ...8
      k4: .... .... .... ..10
      k5: .... .... .... ..20
      k6: .... .... .... ..40
      k7: .... .... .... ..80
      k7: .... .... ...A .F35
  END


=head3 At a point

Load data into a zmm register at the indoicated point and retrieve data fromn a zmm regisiter at the indicated ppint.

=head4 InsertZeroIntoRegisterAtPoint($point, $in)

Insert a zero into the specified register at the point indicated by another general purpose or mask register moving the higher bits one position to the left.

     Parameter  Description
  1  $point     Register with a single 1 at the insertion point
  2  $in        Register to be inserted into.

B<Example:>


    Mov r15, 0x100;                                                               # Given a register with a single one in it indicating the desired position,
    Mov r14, 0xFFDC;                                                              # Insert a zero into the register at that position shifting the bits above that position up left one to make space for the new zero.
    Mov r13, 0xF03F;
    PrintOutRegisterInHex         r14, r15;

    InsertZeroIntoRegisterAtPoint r15, r14;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex r14;
    Or r14, r15;                                                                  # Replace the inserted zero with a one
    PrintOutRegisterInHex r14;
    InsertOneIntoRegisterAtPoint r15, r13;
    PrintOutRegisterInHex r13;
    ok Assemble(debug => 0, eq => <<END, avx512=>0);
     r14: .... .... .... FFDC
     r15: .... .... .... .1..
     r14: .... .... ...1 FEDC
     r14: .... .... ...1 FFDC
     r13: .... .... ...1 E13F
  END


=head4 InsertOneIntoRegisterAtPoint($point, $in)

Insert a one into the specified register at the point indicated by another register.

     Parameter  Description
  1  $point     Register with a single 1 at the insertion point
  2  $in        Register to be inserted into.

B<Example:>


    Mov r15, 0x100;                                                               # Given a register with a single one in it indicating the desired position,
    Mov r14, 0xFFDC;                                                              # Insert a zero into the register at that position shifting the bits above that position up left one to make space for the new zero.
    Mov r13, 0xF03F;
    PrintOutRegisterInHex         r14, r15;
    InsertZeroIntoRegisterAtPoint r15, r14;
    PrintOutRegisterInHex r14;
    Or r14, r15;                                                                  # Replace the inserted zero with a one
    PrintOutRegisterInHex r14;

    InsertOneIntoRegisterAtPoint r15, r13;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex r13;
    ok Assemble(debug => 0, eq => <<END, avx512=>0);
     r14: .... .... .... FFDC
     r15: .... .... .... .1..
     r14: .... .... ...1 FEDC
     r14: .... .... ...1 FFDC
     r13: .... .... ...1 E13F
  END


=head1 Comparison codes

The codes used to specify what sort of comparison to perform

=head1 Structured Programming

Structured programming constructs

=head2 If

If statements

=head3 If($jump, $then, $else)

If statement.

     Parameter  Description
  1  $jump      Jump op code of variable
  2  $then      Then - required
  3  $else      Else - optional

B<Example:>


    my $n0 = K(zero => 0);

    If $n0 == 0,  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "zero == 0";
     },
    Ef {$n0 == 1}
    Then
     {PrintOutStringNL "zero == 1";
     },
    Else
     {PrintOutStringNL "zero == 2";
     };

    my $n1 = K(one => 1);

    If $n1 == 0,  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "one == 0";
     },
    Ef {$n1 == 1}
    Then
     {PrintOutStringNL "one == 1";
     },
    Else
     {PrintOutStringNL "one == 2";
     };

    my $n2 = K(two => 2);

    If $n2 == 0,  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "two == 0";
     },
    Ef {$n2 == 1}
    Then
     {PrintOutStringNL "two == 1";
     },
    Else
     {PrintOutStringNL "two == 2";
     };

    ok Assemble eq => <<END, avx512=>0;
  zero == 0
  one == 1
  two == 2
  END

    my $a = K(key => 1);

    If $a > 0,  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then {Mov rax, 1},
    Else {Mov rax, 2};
    PrintOutRegisterInHex rax;

    ok Assemble eq=><<END, avx512=>1;
     rax: .... .... .... ...1
  END


=head3 Then($block)

Then block for an If statement.

     Parameter  Description
  1  $block     Then block

B<Example:>


    my $n0 = K(zero => 0);
    If $n0 == 0,

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "zero == 0";
     },
    Ef {$n0 == 1}

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "zero == 1";
     },
    Else
     {PrintOutStringNL "zero == 2";
     };

    my $n1 = K(one => 1);
    If $n1 == 0,

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "one == 0";
     },
    Ef {$n1 == 1}

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "one == 1";
     },
    Else
     {PrintOutStringNL "one == 2";
     };

    my $n2 = K(two => 2);
    If $n2 == 0,

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "two == 0";
     },
    Ef {$n2 == 1}

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "two == 1";
     },
    Else
     {PrintOutStringNL "two == 2";
     };

    ok Assemble eq => <<END, avx512=>0;
  zero == 0
  one == 1
  two == 2
  END

    my $a = K(key => 1);
    If $a > 0,

    Then {Mov rax, 1},  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Else {Mov rax, 2};
    PrintOutRegisterInHex rax;

    ok Assemble eq=><<END, avx512=>1;
     rax: .... .... .... ...1
  END

    PrintCString  ($stdout, V(str => Rs("abc\0def")));
    PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));
    ok Assemble eq => <<END;
  abcABC
  END

    my $a = V(a => 3);  $a->outNL;
    my $b = K(b => 2);  $b->outNL;
    my $c = $a +  $b; $c->outNL;
    my $d = $c -  $a; $d->outNL;
    my $g = $a *  $b; $g->outNL;
    my $h = $g /  $b; $h->outNL;
    my $i = $a %  $b; $i->outNL;

    If ($a == 3,

    Then  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "a == 3"
     },
    Else
     {PrintOutStringNL "a != 3"
     });

    ++$a; $a->outNL;
    --$a; $a->outNL;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ...3
  b: .... .... .... ...2
  (a add b): .... .... .... ...5
  ((a add b) sub a): .... .... .... ...2
  (a times b): .... .... .... ...6
  ((a times b) / b): .... .... .... ...3
  (a % b): .... .... .... ...1
  a == 3
  a: .... .... .... ...4
  a: .... .... .... ...3
  END


=head3 Else($block)

Else block for an If statement.

     Parameter  Description
  1  $block     Else block

B<Example:>


    my $n0 = K(zero => 0);
    If $n0 == 0,
    Then
     {PrintOutStringNL "zero == 0";
     },
    Ef {$n0 == 1}
    Then
     {PrintOutStringNL "zero == 1";
     },

    Else  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "zero == 2";
     };

    my $n1 = K(one => 1);
    If $n1 == 0,
    Then
     {PrintOutStringNL "one == 0";
     },
    Ef {$n1 == 1}
    Then
     {PrintOutStringNL "one == 1";
     },

    Else  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "one == 2";
     };

    my $n2 = K(two => 2);
    If $n2 == 0,
    Then
     {PrintOutStringNL "two == 0";
     },
    Ef {$n2 == 1}
    Then
     {PrintOutStringNL "two == 1";
     },

    Else  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "two == 2";
     };

    ok Assemble eq => <<END, avx512=>0;
  zero == 0
  one == 1
  two == 2
  END

    my $a = K(key => 1);
    If $a > 0,
    Then {Mov rax, 1},

    Else {Mov rax, 2};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    ok Assemble eq=><<END, avx512=>1;
     rax: .... .... .... ...1
  END

    PrintCString  ($stdout, V(str => Rs("abc\0def")));
    PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));
    ok Assemble eq => <<END;
  abcABC
  END

    my $a = V(a => 3);  $a->outNL;
    my $b = K(b => 2);  $b->outNL;
    my $c = $a +  $b; $c->outNL;
    my $d = $c -  $a; $d->outNL;
    my $g = $a *  $b; $g->outNL;
    my $h = $g /  $b; $h->outNL;
    my $i = $a %  $b; $i->outNL;

    If ($a == 3,
    Then
     {PrintOutStringNL "a == 3"
     },

    Else  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "a != 3"
     });

    ++$a; $a->outNL;
    --$a; $a->outNL;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ...3
  b: .... .... .... ...2
  (a add b): .... .... .... ...5
  ((a add b) sub a): .... .... .... ...2
  (a times b): .... .... .... ...6
  ((a times b) / b): .... .... .... ...3
  (a % b): .... .... .... ...1
  a == 3
  a: .... .... .... ...4
  a: .... .... .... ...3
  END


=head3 ifOr($conditions, $Then, $Else)

Execute then or else block based on a multiplicity of OR conditions executed until one succeeds.

     Parameter    Description
  1  $conditions  Array of conditions
  2  $Then        Then sub
  3  $Else        Else sub

B<Example:>


    my $a = K key => 1;
    my $b = K key => 1;


    ifOr [sub{$a==$a}, sub{$a==$a}],  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "AAAA11";
     },
    Else
     {PrintOutStringNL "AAAA22";
     };


    ifOr [sub{$a==$a}, sub{$a!=$a}],  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "BBBB11";
     },
     Else
     {PrintOutStringNL "BBBB22";
     };


    ifOr [sub{$a!=$a}, sub{$a==$a}],  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "CCCC11";
     },
     Else
     {PrintOutStringNL "CCCC22";
     };


    ifOr [sub{$a!=$b}, sub{$a!=$b}],  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "DDDD11";
     },
     Else
     {PrintOutStringNL "DDDD22";
     };

    ok Assemble eq => <<END, avx512=>1;
  AAAA11
  BBBB11
  CCCC11
  DDDD22
  END


=head3 ifAnd($conditions, $Then, $Else)

Execute then or else block based on a multiplicity of AND conditions executed until one fails.

     Parameter    Description
  1  $conditions  Array of conditions
  2  $Then        Then sub
  3  $Else        Else sub

B<Example:>


  #latest:


=head3 Ef($condition, $then, $else)

Else if block for an If statement.

     Parameter   Description
  1  $condition  Condition
  2  $then       Then block
  3  $else       Else block

B<Example:>


    my $n0 = K(zero => 0);
    If $n0 == 0,
    Then
     {PrintOutStringNL "zero == 0";
     },

    Ef {$n0 == 1}  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "zero == 1";
     },
    Else
     {PrintOutStringNL "zero == 2";
     };

    my $n1 = K(one => 1);
    If $n1 == 0,
    Then
     {PrintOutStringNL "one == 0";
     },

    Ef {$n1 == 1}  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "one == 1";
     },
    Else
     {PrintOutStringNL "one == 2";
     };

    my $n2 = K(two => 2);
    If $n2 == 0,
    Then
     {PrintOutStringNL "two == 0";
     },

    Ef {$n2 == 1}  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutStringNL "two == 1";
     },
    Else
     {PrintOutStringNL "two == 2";
     };

    ok Assemble eq => <<END, avx512=>0;
  zero == 0
  one == 1
  two == 2
  END


=head3 Via flags

If depending on the flags register.

=head4 IfEq($then, $else)

If equal execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head4 IfNe($then, $else)

If not equal execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head4 IfNz($then, $else)

If the zero flag is not set then execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    Mov rax, 0;
    Test rax,rax;

    IfNz  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutRegisterInHex rax;
     },
    Else
     {PrintOutRegisterInHex rbx;
     };
    Mov rax, 1;
    Test rax,rax;

    IfNz  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Then
     {PrintOutRegisterInHex rcx;
     },
    Else
     {PrintOutRegisterInHex rdx;
     };

    ok Assemble(avx512=>0) =~ m(rbx.*rcx)s;


=head4 IfZ($then, $else)

If the zero flag is set then execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;

    SetZF;

    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ClearZF;
    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head4 IfC($then, $else)

If the carry flag is set then execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;

    SetZF;
    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};
    ClearZF;
    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;

    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head4 IfNc($then, $else)

If the carry flag is not set then execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    SetZF;
    PrintOutZF;
    ClearZF;
    PrintOutZF;

    SetZF;
    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};
    ClearZF;
    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};

    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head4 IfLt($then, $else)

If less than execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head4 IfLe($then, $else)

If less than or equal execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head4 IfGt($then, $else)

If greater than execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head4 IfGe($then, $else)

If greater than or equal execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

B<Example:>


    my $cmp = sub
     {my ($a, $b) = @_;

      for my $op(qw(eq ne lt le gt ge))
       {Mov rax, $a;
        Cmp rax, $b;
        my $Op = ucfirst $op;
        eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
        $@ and confess $@;
       }
     };
    &$cmp(1,1);
    &$cmp(1,2);
    &$cmp(3,2);
    Assemble eq => <<END, avx512=>0;
  1 eq 1
  1 NOT ne 1
  1 NOT lt 1
  1 le 1
  1 NOT gt 1
  1 ge 1
  1 NOT eq 2
  1 ne 2
  1 lt 2
  1 le 2
  1 NOT gt 2
  1 NOT ge 2
  3 NOT eq 2
  3 ne 2
  3 NOT lt 2
  3 NOT le 2
  3 gt 2
  3 ge 2
  END


=head2 Boolean Blocks

Perform blocks depending on boolean conditions

=head3 Pass($block)

Pass block for an L<OrBlock>.

     Parameter  Description
  1  $block     Block

B<Example:>


    Mov rax, 1;
    OrBlock
     {my ($pass, $end, $start) = @_;
      Cmp rax, 1;
      Je  $pass;
      Cmp rax, 2;
      Je  $pass;
      PrintOutStringNL "Fail";
     }

    Pass  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($end, $pass, $start) = @_;

      PrintOutStringNL "Pass";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     };

    ok Assemble eq => <<END, avx512=>0;

  Pass  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  END


=head3 Fail($block)

Fail block for an L<AndBlock>.

     Parameter  Description
  1  $block     Block

B<Example:>


    Mov rax, 1; Mov rdx, 2;
    AndBlock
     {my ($fail, $end, $start) = @_;
      Cmp rax, 1;
      Jne $fail;
      Cmp rdx, 2;
      Jne $fail;
      PrintOutStringNL "Pass";
     }

    Fail  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($end, $fail, $start) = @_;

      PrintOutStringNL "Fail";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     };

    ok Assemble eq => <<END, avx512=>0;
  Pass
  END


=head3 Block($code)

Execute a block of code with labels supplied for the start and end of this code.

     Parameter  Description
  1  $code      Block of code

B<Example:>


  #latest:


=head3 AndBlock($test, $fail)

Short circuit B<and>: execute a block of code to test conditions which, if all of them pass, allows the first block to continue successfully else if one of the conditions fails we execute the optional fail block.

     Parameter  Description
  1  $test      Block
  2  $fail      Optional failure block

B<Example:>


    Mov rax, 1; Mov rdx, 2;

    AndBlock  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($fail, $end, $start) = @_;
      Cmp rax, 1;
      Jne $fail;
      Cmp rdx, 2;
      Jne $fail;
      PrintOutStringNL "Pass";
     }
    Fail
     {my ($end, $fail, $start) = @_;
      PrintOutStringNL "Fail";
     };

    ok Assemble eq => <<END, avx512=>0;
  Pass
  END


=head3 OrBlock($test, $pass)

Short circuit B<or>: execute a block of code to test conditions which, if one of them is met, leads on to the execution of the pass block, if all of the tests fail we continue withe the test block.

     Parameter  Description
  1  $test      Tests
  2  $pass      Optional block to execute on success

B<Example:>


    Mov rax, 1;

    OrBlock  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($pass, $end, $start) = @_;
      Cmp rax, 1;
      Je  $pass;
      Cmp rax, 2;
      Je  $pass;
      PrintOutStringNL "Fail";
     }
    Pass
     {my ($end, $pass, $start) = @_;
      PrintOutStringNL "Pass";
     };

    ok Assemble eq => <<END, avx512=>0;
  Pass
  END


=head2 Iteration

Iterate with for loops

=head3 For($block, $register, $limit, $increment)

For - iterate the block as long as register is less than limit incrementing by increment each time. Nota Bene: The register is not explicitly set to zero as you might want to start at some other number.

     Parameter   Description
  1  $block      Block
  2  $register   Register
  3  $limit      Limit on loop
  4  $increment  Increment on each iteration

B<Example:>



    For  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($start, $end, $next) = @_;
      Cmp rax, 3;
      Jge $end;
      PrintOutRegisterInHex rax;
     } rax, 16, 1;

    ok Assemble eq => <<END, avx512=>0;
     rax: .... .... .... ...0
     rax: .... .... .... ...1
     rax: .... .... .... ...2
  END


=head3 ToZero($block, $register)

Iterate a block the number of times specified in the register which is decremented to zero.

     Parameter  Description
  1  $block     Block
  2  $register  Limit register

B<Example:>


    Mov rax, 3;

    ToZero  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "AAAA";
     }  rax;
    ok Assemble eq => <<END, clocks=>4374;
  AAAA
  AAAA
  AAAA
  END


=head3 ForIn($full, $last, $register, $limitRegister, $increment)

For - iterate the full block as long as register plus increment is less than than limit incrementing by increment each time then perform the last block with the remainder which might be of length zero.

     Parameter       Description
  1  $full           Block for full block
  2  $last           Block for last block
  3  $register       Register
  4  $limitRegister  Register containing upper limit of loop
  5  $increment      Increment on each iteration

B<Example:>


    my $remainder = r15, my $offset = r14;

    Mov $offset, 0;
    Mov $remainder, 10;


    ForIn  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {PrintOutStringNL "AAAA";
      PrintOutRegisterInHex $offset, $remainder;
     }
    Then
     {PrintOutStringNL "BBBB";
      PrintOutRegisterInHex $offset, $remainder;
     }, $offset, $remainder, 4;

    ok Assemble eq => <<END;
  AAAA
     r14: .... .... .... ...0
     r15: .... .... .... ...A
  AAAA
     r14: .... .... .... ...4
     r15: .... .... .... ...A
  BBBB
     r14: .... .... .... ...8
     r15: .... .... .... ...2
  END


=head3 uptoNTimes($code, $register, $limit)

Execute a block of code up to a constant number of times controlled by the named register.

     Parameter  Description
  1  $code      Block of code
  2  $register  Register controlling loop
  3  $limit     Constant limit

B<Example:>



    uptoNTimes  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($end, $start) = @_;
      PrintOutRegisterInHex rax;
     } rax, 3;

    ok Assemble eq => <<END;
     rax: .... .... .... ...3
     rax: .... .... .... ...2
     rax: .... .... .... ...1
  END


=head3 ForEver($block)

Iterate for ever.

     Parameter  Description
  1  $block     Block to iterate

B<Example:>


    my $e = q(readChar);


    ForEver  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($start, $end) = @_;
      ReadChar;
      Cmp rax, 0xa;
      Jle $end;
      PrintOutRaxAsChar;
      PrintOutRaxAsCharNL;
     };
    PrintOutNL;

    Assemble keep => $e;

    my $r = qx(echo "ABCDCBA" | ./$e);
    is_deeply $r, <<END;
  AA
  BB
  CC
  DD
  CC
  BB
  AA

  END
    unlink $e;


=head2 Subroutine

Create and call subroutines with the option of placing them into an area that can be writtento a file and reloaded and executed by another process.

=head3 Subroutine($block, %options)

Create a subroutine that can be called in assembler code.

     Parameter  Description
  1  $block     Block of code as a sub
  2  %options   Options

B<Example:>


    my $g = V g => 3;

    my $s = Subroutine  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($p, $s, $sub) = @_;
      my $g = $$p{g};
      $g->copy($g - 1);
      $g->outNL;
      If $g > 0,
      Then
       {$sub->call(parameters=>{g => $g});
       };
     } parameters=>[qw(g)], name => 'ref';

    $s->call(parameters=>{g => $g});

    ok Assemble eq => <<END;
  g: .... .... .... ...2
  g: .... .... .... ...1
  g: .... .... .... ...0
  END

    package InnerStructure                                                        # Test passing structures into a subroutine
     {use Data::Table::Text qw(:all);
      sub new($)                                                                  # Create a new structure
       {my ($value) = @_;                                                         # Value for structure variable
        describe(value => Nasm::X86::V(var => $value))
       };
      sub describe(%)                                                             # Describe the components of a structure
       {my (%options) = @_;                                                       # Options
        genHash(__PACKAGE__,
          value => $options{value},
         );
       }
     }

    package OuterStructure
     {use Data::Table::Text qw(:all);
      sub new($$)                                                                 # Create a new structure
       {my ($valueOuter, $valueInner) = @_;                                       # Value for structure variable
        describe
         (value => Nasm::X86::V(var => $valueOuter),
          inner => InnerStructure::new($valueInner),
         )
       };
      sub describe(%)                                                             # Describe the components of a structure
       {my (%options) = @_;                                                       # Options
        genHash(__PACKAGE__,
          value => $options{value},
          inner => $options{inner},
         );
       }
     }

    my $t = OuterStructure::new(42, 4);


    my $s = Subroutine  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my ($parameters, $structures, $sub) = @_;                                   # Variable parameters, structure variables, structure copies, subroutine description

      $$structures{test}->value->setReg(rax);
      Mov r15, 84;
      $$structures{test}->value->getReg(r15);
      Mov r15, 8;
      $$structures{test}->inner->value->getReg(r15);

      $$parameters{p}->setReg(rdx);
     } parameters=>[qw(p)], structures => {test => $t}, name => 'test';

    my $T = OuterStructure::new(42, 4);
    my $V = V parameter => 21;

    $s->call(parameters=>{p => $V}, structures=>{test => $T});

    PrintOutRaxInDecNL;
    Mov rax, rdx;
    PrintOutRaxInDecNL;
    $t->value->outInDecNL;
    $t->inner->value->outInDecNL;
    $T->value->outInDecNL;
    $T->inner->value->outInDecNL;
    ok Assemble eq => <<END, avx512=>0;
  42
  21
  var: 42
  var: 4
  var: 84
  var: 8
  END


    my $s = Subroutine                                                              # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



    my $t = Subroutine                                                              # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



=head3 Nasm::X86::Area::writeLibraryHeader($area, $subs)

Load a hash of subroutine names and offsets into an area

     Parameter  Description
  1  $area      Area to load into
  2  $subs      Hash of subroutine names to offsets

B<Example:>


    my $a = CreateArea;

    my $u = $a->CreateTree(stringTree=>1);
    $u->putKeyString(constantString("ab"), K key => 1);
    $u->putKeyString(constantString("ac"), K key => 3);
    $u->putKeyString(constantString("𝕒𝕒"), K key => 4);

    my %s = (ab => 2, 𝕒𝕒 =>8, 𝗮𝗮𝗮=>12, 𝝰𝝰𝝰𝝰=>16);                                 # Subroutine names and offsets

    $a->writeLibraryHeader({%s});
    my ($inter, $subroutines) = $a->readLibraryHeader($u);

    $inter->dump("TT");

    ok Assemble eq => <<END, clocks=>4374;
  TT
  At:  1C0                    length:    2,  data:  200,  nodes:  240,  first:   40, root, leaf
    Index:    0    1
    Keys :    1    4
    Data :    2    8
  end
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    K(loop => 16)->for(sub
     {my ($i, $start, $next, $end) = @_;
      my $T = $a->CreateTree;
      $t->put($i, $T);
      $t->size->outNL;
     });

    K(loop => 16)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->popSubTree;
      $t->size->outNL;
     });

    ok Assemble eq => <<END, avx512=>1;
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ..10
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;
    $t->push($T);
    $a->dump("AA");
    $t->popSubTree;
    $a->dump("BB");

    ok Assemble eq => <<END, avx512=>1;
  AA
  Area     Size:     4096    Used:      384
  .... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | C0__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
  BB
  Area     Size:     4096    Used:      384
  .... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
  END


=head3 Nasm::X86::Subroutine::call($sub, %options)

Call a sub optionally passing it parameters.

     Parameter  Description
  1  $sub       Subroutine descriptor
  2  %options   Options

B<Example:>



   my $h = genHash("AAAA",
     a => V(a =>  1),
     b => V(b =>  2),
     c => V(c =>  3),
     d => V(d =>  4),
     e => V(e =>  5),
     f => V(f =>  6),
     g => V(g =>  7),
     h => V(h =>  8),
     i => V(i =>  9),
     j => V(j => 10),
     k => V(k => 11),
     l => V(l => 12));

   my $i = genHash("AAAA",
     a => V(a => 0x011),
     b => V(b => 0x022),
     c => V(c => 0x033),
     d => V(d => 0x044),
     e => V(e => 0x055),
     f => V(f => 0x066),
     g => V(g => 0x077),
     h => V(h => 0x088),
     i => V(i => 0x099),
     j => V(j => 0x111),
     k => V(k => 0x222),
     l => V(l => 0x333));

    my $s = Subroutine
     {my ($p, $s, $sub) = @_;
      my $h = $$s{h};
      my $a = $$p{a};
      $$h{a}->outNL;
      $$h{b}->outNL;
      $$h{c}->outNL;
      $$h{d}->outNL;
      $$h{e}->outNL;
      $$h{f}->outNL;
      $$h{g}->outNL;
      $$h{h}->outNL;
      $$h{i}->outNL;
      $$h{j}->outNL;
      $$h{k}->outNL;
      $$h{l}->outNL;
      $$p{b}->outNL;
     } name => "s", structures => {h => $h}, parameters=>[qw(a b)];

    $s->call(structures => {h => $i}, parameters=>{a=>V(key => 1), b=>V(key => 0x111)});
    $s->call(structures => {h => $h}, parameters=>{a=>V(key => 2), b=>V(key => 0x222)});

    Assemble eq=><<END, clocks=>9151, label => 'aa';
  a: .... .... .... ..11
  b: .... .... .... ..22
  c: .... .... .... ..33
  d: .... .... .... ..44
  e: .... .... .... ..55
  f: .... .... .... ..66
  g: .... .... .... ..77
  h: .... .... .... ..88
  i: .... .... .... ..99
  j: .... .... .... .111
  k: .... .... .... .222
  l: .... .... .... .333
  b: .... .... .... .111
  a: .... .... .... ...1
  b: .... .... .... ...2
  c: .... .... .... ...3
  d: .... .... .... ...4
  e: .... .... .... ...5
  f: .... .... .... ...6
  g: .... .... .... ...7
  h: .... .... .... ...8
  i: .... .... .... ...9
  j: .... .... .... ...A
  k: .... .... .... ...B
  l: .... .... .... ...C
  b: .... .... .... .222
  END


   my $h = genHash("AAAA",
     a => V(a =>  1),
     b => V(b =>  2),
     c => V(c =>  3),
     d => V(d =>  4),
     e => V(e =>  5),
     f => V(f =>  6),
     g => V(g =>  7),
     h => V(h =>  8),
     i => V(i =>  9),
     j => V(j => 10),
     k => V(k => 11),
     l => V(l => 12));

    my $s = Subroutine
     {my ($p, $s, $sub) = @_;
      my $h = $$s{h};
      my $a = $$p{a};
      $$h{a}->outNL;
      $$h{b}->outNL;
      $$h{c}->outNL;
      $$h{d}->outNL;
      $$h{e}->outNL;
      $$h{f}->outNL;
      $$h{g}->outNL;
      $$h{h}->outNL;
      $$h{i}->outNL;
      $$h{j}->outNL;
      $$h{k}->outNL;
      $$h{l}->outNL;
      If $a > 0,
      Then
       {$sub->call(structures => {h => $h}, parameters=>{a=>V(key => 0), b=>V(key => 0x111)});
       };
     } name => "s", structures => {h => $h}, parameters=>[qw(a b)];

    $s->call(structures => {h => $h}, parameters=>{a=>V(key => 2), b=>V(key => 0x222)});

    Assemble eq=><<END, clocks=>17609, label => 'aaa';
  a: .... .... .... ...1
  b: .... .... .... ...2
  c: .... .... .... ...3
  d: .... .... .... ...4
  e: .... .... .... ...5
  f: .... .... .... ...6
  g: .... .... .... ...7
  h: .... .... .... ...8
  i: .... .... .... ...9
  j: .... .... .... ...A
  k: .... .... .... ...B
  l: .... .... .... ...C
  a: .... .... .... ...1
  b: .... .... .... ...2
  c: .... .... .... ...3
  d: .... .... .... ...4
  e: .... .... .... ...5
  f: .... .... .... ...6
  g: .... .... .... ...7
  h: .... .... .... ...8
  i: .... .... .... ...9
  j: .... .... .... ...A
  k: .... .... .... ...B
  l: .... .... .... ...C
  END


=head3 Nasm::X86::Subroutine::inline($sub, %options)

Call a sub by in-lining it, optionally passing it parameters.

     Parameter  Description
  1  $sub       Subroutine descriptor
  2  %options   Options

B<Example:>


    my $s = Subroutine                                                            # Load and print rax
     {my ($p, $s, $sub) = @_;
      $$p{ppp}->outNL;
     } name => "s", parameters=>[qw(ppp)];

    $s->call  (parameters => {ppp => V ppp => 0x99});                             # Call   378
    $s->inline(parameters => {ppp => V ppp => 0xaa});                             # Inline 364

    Assemble eq=><<END, avx512=>1;
  ppp: .... .... .... ..99
  ppp: .... .... .... ..AA
  END


=head2 Trace back

Generate a subroutine calll trace back

=head3 PrintOutTraceBack($message)

Print sub routine track back on stdout and then exit with a message.

     Parameter  Description
  1  $message   Reason why we are printing the trace back and then stopping

B<Example:>


    my $d = V depth => 3;                                                         # Create a variable on the stack

    my $s = Subroutine
     {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine descriptor
      $$p{depth}->outNL;
      my $d = $$p{depth}->copy($$p{depth} - 1);                                   # Modify the variable referenced by the parameter

      If $d > 0,
      Then
       {$sub->call(parameters => {depth => $d});                                  # Recurse
       };

     } parameters =>[qw(depth)], name => 'ref';

    $s->call(parameters=>{depth => $d});

    $d->outNL;
    ok Assemble eq => <<END, avx512=>0;
  depth: .... .... .... ...3
  depth: .... .... .... ...2
  depth: .... .... .... ...1
  depth: .... .... .... ...0
  END


=head3 OnSegv()

Request a trace back followed by exit on a B<segv> signal.


B<Example:>



    OnSegv();                                                                     # Request a trace back followed by exit on a segv signal.  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $t = Subroutine                                                            # Subroutine that will cause an error to occur to force a trace back to be printed
     {Mov r15, 0;
      Mov r15, "[r15]";                                                           # Try to read an unmapped memory location
     } [qw(in)], name => 'sub that causes a segv';                                # The name that will appear in the trace back

    $t->call(K(in, 42));

    ok Assemble(debug => 0, keep2 => 'signal', avx512=>0, eq => <<END, avx512=>0);# Cannot use the emulator because it does not understand signals

  Subroutine trace back, depth:  1
  0000 0000 0000 002A    sub that causes a segv

  END


=head2 Comments

Inserts comments into the generated assember code.

=head3 Comment(@comment)

Insert a comment into the assembly code.

     Parameter  Description
  1  @comment   Text of comment

B<Example:>



    Comment "Print a string from memory";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $s = "Hello World";
    Mov rax, Rs($s);
    Mov rdi, length $s;
    PrintOutMemory;
    Exit(0);

    ok Assemble(avx512=>0) =~ m(Hello World);


=head1 Print

Print the values of registers and memory interspersed with constant strings.  The print commands do not overwrite the free registers as doing so would make debugging difficult.

=head2 Strings

Print constant and variable strings

=head3 PrintOutNL()

Print a new line to stderr.


B<Example:>


    Mov rax, 0x666;
    PrintOutRightInDec rax,  8;

    PrintOutNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble avx512=>0, eq=><<END;
      1638
  END

    my $q = Rs('abababab');
    Mov(rax, "[$q]");
    PrintOutString "rax: ";
    PrintOutRaxInHex;

    PrintOutNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Xor rax, rax;
    PrintOutString "rax: ";
    PrintOutRaxInHex;

    PrintOutNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble avx512=>0, eq=><<END;
  rax: 6261 6261 6261 6261
  rax: .... .... .... ...0
  END


=head3 PrintOutString(@string)

Print a constant string to stdout.

     Parameter  Description
  1  @string    String

B<Example:>


    Mov rax, 0x666;
    PrintOutRightInDec rax,  8;
    PrintOutNL;

    ok Assemble avx512=>0, eq=><<END;
      1638
  END

    my $q = Rs('abababab');
    Mov(rax, "[$q]");

    PrintOutString "rax: ";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRaxInHex;
    PrintOutNL;
    Xor rax, rax;

    PrintOutString "rax: ";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRaxInHex;
    PrintOutNL;

    ok Assemble avx512=>0, eq=><<END;
  rax: 6261 6261 6261 6261
  rax: .... .... .... ...0
  END


=head3 PrintOutStringNL(@string)

Print a constant string to stdout followed by a new line.

     Parameter  Description
  1  @string    String

B<Example:>



    PrintOutStringNL "Hello World";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintOutStringNL "Hello
World";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintErrStringNL "Hello World";

    ok Assemble eq => <<END, avx512=>0, label=>'t1';
  Hello World
  Hello
  World
  END


=head3 PrintOutSpace($spaces)

Print a constant number of spaces to stdout.

     Parameter  Description
  1  $spaces    Number of spaces if not one.

B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;

    PrintOutSpace;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRaxInDecNL;

    PrintOutRax_InHex;

    PrintOutSpace;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;

    PrintOutSpace;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRightInDec K(key => 17), K width => 2;

    PrintOutSpace;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head2 Registers

Print selected registers in a variety of formats.

=head3 PrintOutRaxInHex()

Write the content of register rax in hexadecimal in big endian notation to stout.


B<Example:>


    Mov rax, 0x666;
    PrintOutRightInDec rax,  8;
    PrintOutNL;

    ok Assemble avx512=>0, eq=><<END;
      1638
  END

    my $q = Rs('abababab');
    Mov(rax, "[$q]");
    PrintOutString "rax: ";

    PrintOutRaxInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;
    Xor rax, rax;
    PrintOutString "rax: ";

    PrintOutRaxInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;

    ok Assemble avx512=>0, eq=><<END;
  rax: 6261 6261 6261 6261
  rax: .... .... .... ...0
  END


=head3 PrintOutRaxInHexNL()

Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.


B<Example:>


    my $s = Rb(0..255);

    Vmovdqu64 xmm1, "[$s]";
    PrintOutRegisterInHex xmm1;
    PrintOutRegisterInHex xmm1;

    Vmovdqu64 ymm1, "[$s]";
    PrintOutRegisterInHex ymm1;
    PrintOutRegisterInHex ymm1;

    Vmovdqu64 zmm1, "[$s]";
    PrintOutRegisterInHex zmm1;
    PrintOutRegisterInHex zmm1;

    ok Assemble eq =><<END;
    xmm1: .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    xmm1: .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    ymm1: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    ymm1: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    zmm1: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    zmm1: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  END


=head3 PrintOutRax_InHex()

Write the content of register rax in hexadecimal in big endian notation to stout.


B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;


    PrintOutRax_InHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRax_InHexNL()

Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.


B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;

    PrintOutRax_InHexNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRaxInReverseInHex()

Write the content of register rax to stderr in hexadecimal in little endian notation.


B<Example:>


    Mov rax, 0x07654321;
    Shl rax, 32;
    Or  rax, 0x07654321;
    PushR rax;

    PrintOutRaxInHex;
    PrintOutNL;

    PrintOutRaxInReverseInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;

    Mov rax, rsp;
    Mov rdi, 8;
    PrintOutMemoryInHex;
    PrintOutNL;
    PopR rax;

    Mov rax, 4096;
    PushR rax;
    Mov rax, rsp;
    Mov rdi, 8;
    PrintOutMemoryInHex;
    PrintOutNL;
    PopR rax;

    ok Assemble eq => <<END, avx512=>0;
  .765 4321 .765 4321
  2143 65.7 2143 65.7
  2143 65.7 2143 65.7
  ..10 .... .... ....
  END


=head3 PrintOutOneRegisterInHex($r)

Print the named register as a hex string on stdout.

     Parameter  Description
  1  $r         Register to print

B<Example:>


    Mov rax, 0x22;
    Mov rbx, 0x33;

    PrintOutOneRegisterInHex   rax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutOneRegisterInHexNL rbx;

    ok Assemble eq => <<END;
  .... .... .... ..22.... .... .... ..33
  END

    Mov rax, 0x61;
    PrintOutRaxAsChar;
    Mov rax, 0x62;
    PrintOutRaxAsCharNL;

    ok Assemble eq => <<END;
  ab
  END


=head3 PrintOutOneRegisterInHexNL($r)

Print the named register as a hex string on stdout followed by new line.

     Parameter  Description
  1  $r         Register to print

B<Example:>


    Mov rax, 0x22;
    Mov rbx, 0x33;
    PrintOutOneRegisterInHex   rax;

    PrintOutOneRegisterInHexNL rbx;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
  .... .... .... ..22.... .... .... ..33
  END

    Mov rax, 0x61;
    PrintOutRaxAsChar;
    Mov rax, 0x62;
    PrintOutRaxAsCharNL;

    ok Assemble eq => <<END;
  ab
  END


=head3 PrintOutRegisterInHex(@r)

Print the named registers as hex strings on stdout.

     Parameter  Description
  1  @r         Names of the registers to print

B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;


    PrintOutRegisterInHex rbx;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRegistersInHex()

Print the general purpose registers in hex.


B<Example:>


    my $q = Rs('abababab');
    Mov r10, 0x10;
    Mov r11, 0x11;
    Mov r12, 0x12;
    Mov r13, 0x13;
    Mov r14, 0x14;
    Mov r15, 0x15;
    Mov  r8, 0x08;
    Mov  r9, 0x09;
    Mov rax, 1;
    Mov rbx, 2;
    Mov rcx, 3;
    Mov rdi, 4;
    Mov rdx, 5;
    Mov rsi, 6;

    PrintOutRegistersInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $r = Assemble avx512=>0, eq=><<END;
  rfl: .... .... .... .2.2
  r10: .... .... .... ..10
  r11: .... .... .... .2.6
  r12: .... .... .... ..12
  r13: .... .... .... ..13
  r14: .... .... .... ..14
  r15: .... .... .... ..15
   r8: .... .... .... ...8
   r9: .... .... .... ...9
  rax: .... .... .... ...1
  rbx: .... .... .... ...2
  rcx: .... .... ..40 197F
  rdi: .... .... .... ...4
  rdx: .... .... .... ...5
  rsi: .... .... .... ...6
  END


=head2 Zero Flag

Print zero flag

=head3 PrintOutZF()

Print the zero flag without disturbing it on stdout.


B<Example:>


    SetZF;

    PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ClearZF;

    PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    SetZF;

    PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    SetZF;

    PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ClearZF;

    PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    SetZF;
    IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};
    ClearZF;
    IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

    Mov r15, 5;
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
    Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

    ok Assemble eq => <<END, avx512=>0;
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  Zero
  NOT zero
  Carry
  NO carry
  Carry
  NO carry
  END


=head2 Hexadecimal

Print numbers in hexadecimal right justified in a field

=head3 PrintOutRightInHex($number, $width)

Write the specified variable in hexadecimal right justified in a field of specified width on stdout.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;

    PrintOutRightInHex K(key => 17), K width => 2;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRightInHexNL($number, $width)

Write the specified variable in hexadecimal right justified in a field of specified width on stdout followed by a new line.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

B<Example:>


    my $N = K number => 0x12345678;

    for my $i(reverse 1..16)

     {PrintOutRightInHexNL $N, $i;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     }
    ok Assemble eq => <<END;
          12345678
         12345678
        12345678
       12345678
      12345678
     12345678
    12345678
   12345678
  12345678
  2345678
  345678
  45678
  5678
  678
  78
  8
  END

    Mov rax, 0x2a;
    PrintOutRightInDecNL rax, 16;

    PrintOutRightInHexNL rax, 16;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRightInBinNL rax, 16;

    ok Assemble eq => <<END, avx512=>1;
                42
                2A
            101010
  END


=head2 Binary

Print numbers in binary right justified in a field

=head3 PrintOutRightInBin($number, $width)

Write the specified variable in binary right justified in a field of specified width on stdout.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;


    PrintOutRightInBin K(key => 17), K width => 16;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRightInBinNL($number, $width)

Write the specified variable in binary right justified in a field of specified width on stdout followed by a new line.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

B<Example:>


    K(count => 64)->for(sub
     {my ($index, $start, $next, $end) = @_;

      PrintOutRightInBinNL K(number => 0x99), K(max => 64) - $index;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     });
    ok Assemble(avx512=>0, eq => <<END);
                                                          10011001
                                                         10011001
                                                        10011001
                                                       10011001
                                                      10011001
                                                     10011001
                                                    10011001
                                                   10011001
                                                  10011001
                                                 10011001
                                                10011001
                                               10011001
                                              10011001
                                             10011001
                                            10011001
                                           10011001
                                          10011001
                                         10011001
                                        10011001
                                       10011001
                                      10011001
                                     10011001
                                    10011001
                                   10011001
                                  10011001
                                 10011001
                                10011001
                               10011001
                              10011001
                             10011001
                            10011001
                           10011001
                          10011001
                         10011001
                        10011001
                       10011001
                      10011001
                     10011001
                    10011001
                   10011001
                  10011001
                 10011001
                10011001
               10011001
              10011001
             10011001
            10011001
           10011001
          10011001
         10011001
        10011001
       10011001
      10011001
     10011001
    10011001
   10011001
  10011001
  0011001
  011001
  11001
  1001
  001
  01
  1
  END

    Mov rax, 0x2a;
    PrintOutRightInDecNL rax, 16;
    PrintOutRightInHexNL rax, 16;

    PrintOutRightInBinNL rax, 16;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END, avx512=>1;
                42
                2A
            101010
  END


=head2 Decimal

Print numbers in decimal right justified in fields of specified width.

=head3 PrintOutRaxInDec()

Print rax in decimal on stdout.


B<Example:>


    Mov rax, 42;
    Mov rbx, 21;

    PrintOutRaxInDec;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRaxInDecNL()

Print rax in decimal on stdout followed by a new line.


B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;

    PrintOutRaxInDecNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;
    PrintOutRightInDec K(key => 17), K width => 2;
    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRightInDec($number, $width)

Print a variable or register in decimal right justified in a field of the specified width on stdout.

     Parameter  Description
  1  $number    Number as a variable or a register
  2  $width     Width as a variable or constant

B<Example:>


    Mov rax, 42;
    Mov rbx, 21;
    PrintOutRaxInDec;
    PrintOutSpace;
    PrintOutRaxInDecNL;

    PrintOutRax_InHex;
    PrintOutSpace;
    PrintOutRax_InHexNL;

    PrintOutRegisterInHex rbx;
    PrintOutNL;

    PrintOutRightInBin K(key => 17), K width => 16;
    PrintOutSpace;

    PrintOutRightInDec K(key => 17), K width => 2;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutSpace;
    PrintOutRightInHex K(key => 17), K width => 2;
    PrintOutNL;

    ok Assemble eq => <<END;
  42 42
  ____ ____ ____ __2A ____ ____ ____ __2A
     rbx: .... .... .... ..15

             10001 17 11
  END


=head3 PrintOutRightInDecNL($number, $width)

Print a variable or register in decimal right justified in a field of the specified width on stdout followed by a new line.

     Parameter  Description
  1  $number    Number as a variable or a register
  2  $width     Width as a variable or constant

B<Example:>


    Mov rax, 0x2a;

    PrintOutRightInDecNL rax, 16;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRightInHexNL rax, 16;
    PrintOutRightInBinNL rax, 16;

    ok Assemble eq => <<END, avx512=>1;
                42
                2A
            101010
  END


=head2 Text

Print the contents of a register as text.

=head3 PrintOutRaxAsText()

Print rax as text on stdout.


B<Example:>


    Mov rax, 0x636261;

    PrintOutRaxAsText;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, 0x64636261;
    PrintOutRaxAsTextNL;

    ok Assemble eq => <<END;
  abcabcd
  END


=head3 PrintOutRaxAsTextNL()

Print rax as text on stdout followed by a new line.


B<Example:>


    Mov rax, 0x636261;
    PrintOutRaxAsText;
    Mov rax, 0x64636261;

    PrintOutRaxAsTextNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
  abcabcd
  END

    my $t = Rs('abcdefghi');
    Mov rax, $t;
    Mov rax, "[rax]";

    PrintOutRaxAsTextNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ok Assemble eq => <<END, avx512=>0;
  abcdefgh
  END
  }

  #latest:
  if (1) {                                                                         ;
    my $e = q(parameters);

    (V string => "[rbp+8]")->outInDecNL;
    (V string => "[rbp+16]")->outCStringNL;
    (V string => "[rbp+24]")->outCStringNL;
    (V string => "[rbp+32]")->outCStringNL;
    (V string => "[rbp+40]")->outCStringNL;
    (V string => "[rbp+48]")->outInDecNL;

    (V string => "[rbp+8]")->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(rax);
      Inc rax;
      PrintOutRaxInDec;
      Inc rax;
      PrintOutString " : ";
      Shl rax, 3;
      (V string => "[rbp+rax]")->outCStringNL;
     });

    Assemble keep => $e;

    is_deeply scalar(qx(./$e AaAaAaAaAa BbCcDdEe 123456789)), <<END;
  string: 4
  ./parameters
  AaAaAaAaAa
  BbCcDdEe
  123456789
  string: 0
  1 : ./parameters
  2 : AaAaAaAaAa
  3 : BbCcDdEe
  4 : 123456789
  END

    unlink $e;

    K( loop => 16)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(rax);
      Add rax, 0xb0;   Shl rax, 16;
      Mov  ax, 0x9d9d; Shl rax, 8;
      Mov  al, 0xf0;
      PrintOutRaxAsText;
     });
    PrintOutNL;

    ok Assemble eq => <<END, avx512 => 0;
  𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿
  END


=head3 PrintOutRaxAsChar()

Print the character in rax on stdout.


B<Example:>


    my $e = q(readChar);

    ForEver
     {my ($start, $end) = @_;
      ReadChar;
      Cmp rax, 0xa;
      Jle $end;

      PrintOutRaxAsChar;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      PrintOutRaxAsCharNL;
     };
    PrintOutNL;

    Assemble keep => $e;

    my $r = qx(echo "ABCDCBA" | ./$e);
    is_deeply $r, <<END;
  AA
  BB
  CC
  DD
  CC
  BB
  AA

  END
    unlink $e;


=head3 PrintOutRaxAsCharNL()

Print the character in rax on stdout followed by a new line.


B<Example:>


    my $e = q(readChar);

    ForEver
     {my ($start, $end) = @_;
      ReadChar;
      Cmp rax, 0xa;
      Jle $end;
      PrintOutRaxAsChar;

      PrintOutRaxAsCharNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     };
    PrintOutNL;

    Assemble keep => $e;

    my $r = qx(echo "ABCDCBA" | ./$e);
    is_deeply $r, <<END;
  AA
  BB
  CC
  DD
  CC
  BB
  AA

  END
    unlink $e;


=head1 Variables

Variable definitions and operations

=head2 Definitions

Variable definitions

=head3 Nasm::X86::Variable::at($variable)

Return a "[register expression]" to address the data in the variable in the current stack frame.

     Parameter  Description
  1  $variable  Variable descriptor

B<Example:>


  if (1)
   {my $v = V var => 2;
    Mov rax, $v->at;
    PrintOutRegisterInHex rax;
    ok Assemble eq=><<END, avx512=>1;
     rax: .... .... .... ...2
  END
   }

  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 K($name, $expr)

Define a constant variable.

     Parameter  Description
  1  $name      Name of variable
  2  $expr      Initializing expression

B<Example:>



    my $a = K abc => 0x123;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';

    K(key => 2)->outSpaces;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END

    my $s = Subroutine
     {my ($p) = @_;
      $$p{v}->copy($$p{v} + $$p{k} + $$p{g} + 1);
     } name => 'add', parameters=>[qw(v k g)];

    my $v = V(v => 1);

    my $k = K(k => 2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $g = V(g => 3);
    $s->call(parameters=>{v=>$v, k=>$k, g=>$g});
    $v->outNL;

    ok Assemble eq => <<END, avx512=>0;
  v: .... .... .... ...7
  END

    my $g = V g => 0;
    my $s = Subroutine
     {my ($p) = @_;

      $$p{g}->copy(K value => 1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     } name => 'ref2', parameters=>[qw(g)];

    my $t = Subroutine
     {my ($p) = @_;
      $s->call(parameters=>{g=>$$p{g}});
     } name => 'ref', parameters=>[qw(g)];

    $t->call(parameters=>{g=>$g});
    $g->outNL;

    ok Assemble eq => <<END, avx512=>0;
  g: .... .... .... ...1
  END

    PrintCString  ($stdout, V(str => Rs("abc\0def")));
    PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));
    ok Assemble eq => <<END;
  abcABC
  END

    my $a = V(a => 3);  $a->outNL;

    my $b = K(b => 2);  $b->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $c = $a +  $b; $c->outNL;
    my $d = $c -  $a; $d->outNL;
    my $g = $a *  $b; $g->outNL;
    my $h = $g /  $b; $h->outNL;
    my $i = $a %  $b; $i->outNL;

    If ($a == 3,
    Then
     {PrintOutStringNL "a == 3"
     },
    Else
     {PrintOutStringNL "a != 3"
     });

    ++$a; $a->outNL;
    --$a; $a->outNL;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ...3
  b: .... .... .... ...2
  (a add b): .... .... .... ...5
  ((a add b) sub a): .... .... .... ...2
  (a times b): .... .... .... ...6
  ((a times b) / b): .... .... .... ...3
  (a % b): .... .... .... ...1
  a == 3
  a: .... .... .... ...4
  a: .... .... .... ...3
  END


=head3 V($name, $expr)

Define a variable.

     Parameter  Description
  1  $name      Name of variable
  2  $expr      Initializing expression

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END

    my $s = Subroutine
     {my ($p) = @_;
      $$p{v}->copy($$p{v} + $$p{k} + $$p{g} + 1);
     } name => 'add', parameters=>[qw(v k g)];


    my $v = V(v => 1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $k = K(k => 2);

    my $g = V(g => 3);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $s->call(parameters=>{v=>$v, k=>$k, g=>$g});
    $v->outNL;

    ok Assemble eq => <<END, avx512=>0;
  v: .... .... .... ...7
  END


    my $g = V g => 0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $s = Subroutine
     {my ($p) = @_;
      $$p{g}->copy(K value => 1);
     } name => 'ref2', parameters=>[qw(g)];

    my $t = Subroutine
     {my ($p) = @_;
      $s->call(parameters=>{g=>$$p{g}});
     } name => 'ref', parameters=>[qw(g)];

    $t->call(parameters=>{g=>$g});
    $g->outNL;

    ok Assemble eq => <<END, avx512=>0;
  g: .... .... .... ...1
  END


    PrintCString  ($stdout, V(str => Rs("abc\0def")));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ok Assemble eq => <<END;
  abcABC
  END


    my $a = V(a => 3);  $a->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my $b = K(b => 2);  $b->outNL;
    my $c = $a +  $b; $c->outNL;
    my $d = $c -  $a; $d->outNL;
    my $g = $a *  $b; $g->outNL;
    my $h = $g /  $b; $h->outNL;
    my $i = $a %  $b; $i->outNL;

    If ($a == 3,
    Then
     {PrintOutStringNL "a == 3"
     },
    Else
     {PrintOutStringNL "a != 3"
     });

    ++$a; $a->outNL;
    --$a; $a->outNL;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ...3
  b: .... .... .... ...2
  (a add b): .... .... .... ...5
  ((a add b) sub a): .... .... .... ...2
  (a times b): .... .... .... ...6
  ((a times b) / b): .... .... .... ...3
  (a % b): .... .... .... ...1
  a == 3
  a: .... .... .... ...4
  a: .... .... .... ...3
  END


=head2 Print variables

Print the values of variables or the memory addressed by them

=head3 Nasm::X86::Variable::out($left, $title1, $title2)

Dump the value of a variable on stdout.

     Parameter  Description
  1  $left      Left variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

B<Example:>


    my $a = V(a => 1);
    my $b = V(b => 2);
    my $c = $a + $b;
    Mov r15, 22;
    $a->getReg(r15);
    $b->copy($a);
    $b = $b + 1;
    $b->setReg(14);
    $a->outNL;
    $b->outNL;
    $c->out;
    PrintOutNL;
    PrintOutRegisterInHex r14, r15;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ..16
  (b add 1): .... .... .... ..17
  (a add b): .... .... .... ...3
     r14: .... .... .... ..17
     r15: .... .... .... ..16
  END

    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head3 Nasm::X86::Variable::outNL($left, $title1, $title2)

Dump the value of a variable on stdout and append a new line.

     Parameter  Description
  1  $left      Left variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

B<Example:>


    my $a = V a => 0x1111;
    $a->outNL('');
    $a->outRightInBinNL;
    $a->outRightInDecNL;
    $a->outRightInHexNL;
    ok Assemble eq => <<END;
  .... .... .... 1111
     1000100010001
              4369

  END

    my $a = V(a => 1);
    my $b = V(b => 2);
    my $c = $a + $b;
    Mov r15, 22;
    $a->getReg(r15);
    $b->copy($a);
    $b = $b + 1;
    $b->setReg(14);
    $a->outNL;
    $b->outNL;
    $c->out;
    PrintOutNL;
    PrintOutRegisterInHex r14, r15;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ..16
  (b add 1): .... .... .... ..17
  (a add b): .... .... .... ...3
     r14: .... .... .... ..17
     r15: .... .... .... ..16
  END


=head3 Decimal representation

Print out a variable as a decimal number

=head4 Nasm::X86::Variable::outInDec($number, $title1, $title2)

Dump the value of a variable on stdout in decimal.

     Parameter  Description
  1  $number    Number as variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head4 Nasm::X86::Variable::outInDecNL($number, $title1, $title2)

Dump the value of a variable on stdout in decimal followed by a new line.

     Parameter  Description
  1  $number    Number as variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END

    my $e = q(parameters);

    (V string => "[rbp+8]")->outInDecNL;
    (V string => "[rbp+16]")->outCStringNL;
    (V string => "[rbp+24]")->outCStringNL;
    (V string => "[rbp+32]")->outCStringNL;
    (V string => "[rbp+40]")->outCStringNL;
    (V string => "[rbp+48]")->outInDecNL;

    (V string => "[rbp+8]")->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(rax);
      Inc rax;
      PrintOutRaxInDec;
      Inc rax;
      PrintOutString " : ";
      Shl rax, 3;
      (V string => "[rbp+rax]")->outCStringNL;
     });

    Assemble keep => $e;

    is_deeply scalar(qx(./$e AaAaAaAaAa BbCcDdEe 123456789)), <<END;
  string: 4
  ./parameters
  AaAaAaAaAa
  BbCcDdEe
  123456789
  string: 0
  1 : ./parameters
  2 : AaAaAaAaAa
  3 : BbCcDdEe
  4 : 123456789
  END

    unlink $e;


=head3 Decimal representation right justified

Print out a variable as a decimal number right adjusted in a field of specified width

=head4 Nasm::X86::Variable::outRightInDec($number, $width)

Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width.

     Parameter  Description
  1  $number    Number
  2  $width     Width

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head4 Nasm::X86::Variable::outRightInDecNL($number, $width)

Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width followed by a new line.

     Parameter  Description
  1  $number    Number
  2  $width     Width

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head3 Hexadecimal representation, right justified

Print number variables in hexadecimal right justified in fields of specified width.

=head4 Nasm::X86::Variable::outRightInHex($number, $width)

Write the specified variable number in hexadecimal right justified in a field of specified width to stdout.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head4 Nasm::X86::Variable::outRightInHexNL($number, $width)

Write the specified variable number in hexadecimal right justified in a field of specified width to stdout followed by a new line.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head3 Binary representation, right justified

Print number variables in binary right justified in fields of specified width.

=head4 Nasm::X86::Variable::outRightInBin($number, $width)

Write the specified variable number in binary right justified in a field of specified width to stdout.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head4 Nasm::X86::Variable::outRightInBinNL($number, $width)

Write the specified variable number in binary right justified in a field of specified width to stdout followed by a new line.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head3 Spaces

Print out a variable number of spaces.

=head4 Nasm::X86::Variable::spaces($count, $channel)

Print the specified number of spaces to the specified channel.

     Parameter  Description
  1  $count     Number of spaces
  2  $channel   Channel

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head4 Nasm::X86::Variable::outSpaces($count)

Print the specified number of spaces to stdout.

     Parameter  Description
  1  $count     Number of spaces

B<Example:>


    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END


=head3 C style zero terminated strings

Print out C style zero terminated strings.

=head4 Nasm::X86::Variable::outCString($string)

Print a zero terminated C style string addressed by a variable on stdout.

     Parameter  Description
  1  $string    String

B<Example:>


    my $s = Rutf8 '𝝰𝝱𝝲𝝳';
    V(address => $s)->outCString; PrintOutNL;
    V(address => $s)->outCStringNL;

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  𝝰𝝱𝝲𝝳
  𝝰𝝱𝝲𝝳
  END


=head4 Nasm::X86::Variable::outCStringNL($string)

Print a zero terminated C style string addressed by a variable on stdout followed by a new line.

     Parameter  Description
  1  $string    String

B<Example:>


    my $s = Rutf8 '𝝰𝝱𝝲𝝳';
    V(address => $s)->outCString; PrintOutNL;
    V(address => $s)->outCStringNL;

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  𝝰𝝱𝝲𝝳
  𝝰𝝱𝝲𝝳
  END

    my $e = q(parameters);

    (V string => "[rbp+8]")->outInDecNL;
    (V string => "[rbp+16]")->outCStringNL;
    (V string => "[rbp+24]")->outCStringNL;
    (V string => "[rbp+32]")->outCStringNL;
    (V string => "[rbp+40]")->outCStringNL;
    (V string => "[rbp+48]")->outInDecNL;

    (V string => "[rbp+8]")->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(rax);
      Inc rax;
      PrintOutRaxInDec;
      Inc rax;
      PrintOutString " : ";
      Shl rax, 3;
      (V string => "[rbp+rax]")->outCStringNL;
     });

    Assemble keep => $e;

    is_deeply scalar(qx(./$e AaAaAaAaAa BbCcDdEe 123456789)), <<END;
  string: 4
  ./parameters
  AaAaAaAaAa
  BbCcDdEe
  123456789
  string: 0
  1 : ./parameters
  2 : AaAaAaAaAa
  3 : BbCcDdEe
  4 : 123456789
  END

    unlink $e;


=head2 Addressing

Create references to variables and dereference variables

=head3 Nasm::X86::Variable::address($source)

Create a variable that contains the address of another variable.

     Parameter  Description
  1  $source    Source variable

B<Example:>


            V(a => 2);
            V(a => 1);
    my $a = V(a => 0)->address;
    ($a+ 0)->dereference->outNL;
    ($a+ 8)->dereference->outNL;
    ($a+16)->dereference->outNL;
    ($a+16)->update(K key => 3);
    ($a+16)->dereference->outNL;
    ok Assemble eq => <<END;
  deref (addr a add 0): .... .... .... ...0
  deref (addr a add 8): .... .... .... ...1
  deref (addr a add 16): .... .... .... ...2
  deref (addr a add 16): .... .... .... ...3
  END


=head3 Nasm::X86::Variable::dereference($address)

Create a variable that contains the contents of the variable addressed by the specified variable.

     Parameter  Description
  1  $address   Source variable

B<Example:>


            V(a => 2);
            V(a => 1);
    my $a = V(a => 0)->address;
    ($a+ 0)->dereference->outNL;
    ($a+ 8)->dereference->outNL;
    ($a+16)->dereference->outNL;
    ($a+16)->update(K key => 3);
    ($a+16)->dereference->outNL;
    ok Assemble eq => <<END;
  deref (addr a add 0): .... .... .... ...0
  deref (addr a add 8): .... .... .... ...1
  deref (addr a add 16): .... .... .... ...2
  deref (addr a add 16): .... .... .... ...3
  END


=head3 Nasm::X86::Variable::update($address, $content)

Update the content of the addressed variable with the content of the specified variable.

     Parameter  Description
  1  $address   Source variable
  2  $content   Content

B<Example:>


            V(a => 2);
            V(a => 1);
    my $a = V(a => 0)->address;
    ($a+ 0)->dereference->outNL;
    ($a+ 8)->dereference->outNL;
    ($a+16)->dereference->outNL;
    ($a+16)->update(K key => 3);
    ($a+16)->dereference->outNL;
    ok Assemble eq => <<END;
  deref (addr a add 0): .... .... .... ...0
  deref (addr a add 8): .... .... .... ...1
  deref (addr a add 16): .... .... .... ...2
  deref (addr a add 16): .... .... .... ...3
  END


=head3 constantString($string)

Return the address and length of a constant string as two variables.

     Parameter  Description
  1  $string    Constant utf8 string

B<Example:>



    my ($t, $l) = constantString("Hello World");  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $t->printOutMemoryNL($l);

    ok Assemble eq => <<END, avx512=>1;
  Hello World
  END


=head2 Operations

Variable operations

=head3 Nasm::X86::Variable::addressExpr($left, $offset)

Create a register expression to address an offset form a variable.

     Parameter  Description
  1  $left      Left variable
  2  $offset    Optional offset

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::clone($variable, $name)

Clone a variable to make a new variable.

     Parameter  Description
  1  $variable  Variable to clone
  2  $name      New name for variable

B<Example:>


    my $a = V('a', 1);
    my $b = $a->clone('a');

    $_->outNL for $a, $b;

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  a: .... .... .... ...1
  a: .... .... .... ...1
  END


=head3 Nasm::X86::Variable::copy($left, $right)

Copy one variable into another.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

B<Example:>


    my $a = V(a => 1);
    my $b = V(b => 2);
    my $c = $a + $b;
    Mov r15, 22;
    $a->getReg(r15);
    $b->copy($a);
    $b = $b + 1;
    $b->setReg(14);
    $a->outNL;
    $b->outNL;
    $c->out;
    PrintOutNL;
    PrintOutRegisterInHex r14, r15;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ..16
  (b add 1): .... .... .... ..17
  (a add b): .... .... .... ...3
     r14: .... .... .... ..17
     r15: .... .... .... ..16
  END

    my $a = K abc => 0x123;
    $a->out;
    PrintOutNL;

    $a->outInDec; PrintOutNL; $a->outInDecNL;
    $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
    $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
    $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

    PrintOutString 'a';
    K(key => 2)->outSpaces;
    PrintOutStringNL 'b';

    ok Assemble eq => <<END, avx512=>1;
  abc: .... .... .... .123
  abc: 291
  abc: 291
         100100011
         100100011
               291
               291
               123

  a  b
  END

    my $s = Subroutine
     {my ($p) = @_;
      $$p{v}->copy($$p{v} + $$p{k} + $$p{g} + 1);
     } name => 'add', parameters=>[qw(v k g)];

    my $v = V(v => 1);
    my $k = K(k => 2);
    my $g = V(g => 3);
    $s->call(parameters=>{v=>$v, k=>$k, g=>$g});
    $v->outNL;

    ok Assemble eq => <<END, avx512=>0;
  v: .... .... .... ...7
  END

    my $g = V g => 0;
    my $s = Subroutine
     {my ($p) = @_;
      $$p{g}->copy(K value => 1);
     } name => 'ref2', parameters=>[qw(g)];

    my $t = Subroutine
     {my ($p) = @_;
      $s->call(parameters=>{g=>$$p{g}});
     } name => 'ref', parameters=>[qw(g)];

    $t->call(parameters=>{g=>$g});
    $g->outNL;

    ok Assemble eq => <<END, avx512=>0;
  g: .... .... .... ...1
  END

    my $a = V('a', 1);
    my $r = R('r')->copyRef($a);
    my $R = R('R')->copyRef($r);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $a->copy(2);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $r->copy(3);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $R->copy(4);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  a: .... .... .... ...1
  r: .... .... .... ...1
  R: .... .... .... ...1
  a: .... .... .... ...2
  r: .... .... .... ...2
  R: .... .... .... ...2
  a: .... .... .... ...3
  r: .... .... .... ...3
  R: .... .... .... ...3
  a: .... .... .... ...4
  r: .... .... .... ...4
  R: .... .... .... ...4
  END


=head3 Nasm::X86::Variable::copyRef($left, $right)

Copy a reference to a variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

B<Example:>


    my $a = V('a', 1);
    my $r = R('r')->copyRef($a);
    my $R = R('R')->copyRef($r);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $a->copy(2);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $r->copy(3);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    $R->copy(4);

    $a->outNL;
    $r->outNL;
    $R->outNL;

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  a: .... .... .... ...1
  r: .... .... .... ...1
  R: .... .... .... ...1
  a: .... .... .... ...2
  r: .... .... .... ...2
  R: .... .... .... ...2
  a: .... .... .... ...3
  r: .... .... .... ...3
  R: .... .... .... ...3
  a: .... .... .... ...4
  r: .... .... .... ...4
  R: .... .... .... ...4
  END


=head3 Nasm::X86::Variable::copyZF($var)

Copy the current state of the zero flag into a variable.

     Parameter  Description
  1  $var       Variable

B<Example:>


    Mov r15, 1;
    my $z = V(zf => undef);
    Cmp r15, 1; $z->copyZF;         $z->outNL;
    Cmp r15, 2; $z->copyZF;         $z->outNL;
    Cmp r15, 1; $z->copyZFInverted; $z->outNL;
    Cmp r15, 2; $z->copyZFInverted; $z->outNL;

    ok Assemble eq => <<END, avx512=>0;
  zf: .... .... .... ...1
  zf: .... .... .... ...0
  zf: .... .... .... ...0
  zf: .... .... .... ...1
  END


=head3 Nasm::X86::Variable::copyZFInverted($var)

Copy the opposite of the current state of the zero flag into a variable.

     Parameter  Description
  1  $var       Variable

B<Example:>


    Mov r15, 1;
    my $z = V(zf => undef);
    Cmp r15, 1; $z->copyZF;         $z->outNL;
    Cmp r15, 2; $z->copyZF;         $z->outNL;
    Cmp r15, 1; $z->copyZFInverted; $z->outNL;
    Cmp r15, 2; $z->copyZFInverted; $z->outNL;

    ok Assemble eq => <<END, avx512=>0;
  zf: .... .... .... ...1
  zf: .... .... .... ...0
  zf: .... .... .... ...0
  zf: .... .... .... ...1
  END


=head3 Nasm::X86::Variable::isRef($variable)

Check whether the specified  variable is a reference to another variable.

     Parameter  Description
  1  $variable  Variable

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::setReg($variable, $register)

Set the named registers from the content of the variable.

     Parameter  Description
  1  $variable  Variable
  2  $register  Register to load

B<Example:>


    my $a = V(a => 1);
    my $b = V(b => 2);
    my $c = $a + $b;
    Mov r15, 22;
    $a->getReg(r15);
    $b->copy($a);
    $b = $b + 1;
    $b->setReg(14);
    $a->outNL;
    $b->outNL;
    $c->out;
    PrintOutNL;
    PrintOutRegisterInHex r14, r15;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ..16
  (b add 1): .... .... .... ..17
  (a add b): .... .... .... ...3
     r14: .... .... .... ..17
     r15: .... .... .... ..16
  END


=head3 Nasm::X86::Variable::getReg($variable, $register)

Load the variable from a register expression.

     Parameter  Description
  1  $variable  Variable
  2  $register  Register expression to load

B<Example:>


    my $a = V(a => 1);
    my $b = V(b => 2);
    my $c = $a + $b;
    Mov r15, 22;
    $a->getReg(r15);
    $b->copy($a);
    $b = $b + 1;
    $b->setReg(14);
    $a->outNL;
    $b->outNL;
    $c->out;
    PrintOutNL;
    PrintOutRegisterInHex r14, r15;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ..16
  (b add 1): .... .... .... ..17
  (a add b): .... .... .... ...3
     r14: .... .... .... ..17
     r15: .... .... .... ..16
  END


=head3 Nasm::X86::Variable::min($left, $right)

Minimum of two variables.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable or constant

B<Example:>


    my $a = V("a", 1);
    my $b = V("b", 2);
    my $c = $a->min($b);
    my $d = $a->max($b);
    $a->outNL;
    $b->outNL;
    $c->outNL;
    $d->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  a: .... .... .... ...1
  b: .... .... .... ...2
  min: .... .... .... ...1
  max: .... .... .... ...2
  END


=head3 Nasm::X86::Variable::max($left, $right)

Maximum of two variables.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable or constant

B<Example:>


    my $a = V("a", 1);
    my $b = V("b", 2);
    my $c = $a->min($b);
    my $d = $a->max($b);
    $a->outNL;
    $b->outNL;
    $c->outNL;
    $d->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  a: .... .... .... ...1
  b: .... .... .... ...2
  min: .... .... .... ...1
  max: .... .... .... ...2
  END


=head3 Nasm::X86::Variable::setMask($start, $length, $mask)

Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.

     Parameter  Description
  1  $start     Variable containing start of mask
  2  $length    Variable containing length of mask
  3  $mask      Mask register

B<Example:>


    my $start  = V("Start",  7);
    my $length = V("Length", 3);
    $start->setMask($length, k7);
    PrintOutRegisterInHex k7;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
      k7: .... .... .... .380
  END

    my $z = V('zero', 0);
    my $o = V('one',  1);
    my $t = V('two',  2);
    $z->setMask($o,       k7); PrintOutRegisterInHex k7;
    $z->setMask($t,       k6); PrintOutRegisterInHex k6;
    $z->setMask($o+$t,    k5); PrintOutRegisterInHex k5;
    $o->setMask($o,       k4); PrintOutRegisterInHex k4;
    $o->setMask($t,       k3); PrintOutRegisterInHex k3;
    $o->setMask($o+$t,    k2); PrintOutRegisterInHex k2;

    $t->setMask($o,       k1); PrintOutRegisterInHex k1;
    $t->setMask($t,       k0); PrintOutRegisterInHex k0;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
      k7: .... .... .... ...1
      k6: .... .... .... ...3
      k5: .... .... .... ...7
      k4: .... .... .... ...2
      k3: .... .... .... ...6
      k2: .... .... .... ...E
      k1: .... .... .... ...4
      k0: .... .... .... ...C
  END


=head3 Nasm::X86::Variable::setMaskFirst($length, $mask)

Set the first bits in the specified mask register.

     Parameter  Description
  1  $length    Variable containing length to set
  2  $mask      Mask register

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::setMaskBit($index, $mask)

Set a bit in the specified mask register retaining the other bits.

     Parameter  Description
  1  $index     Variable containing bit position to set
  2  $mask      Mask register

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::clearMaskBit($index, $mask)

Clear a bit in the specified mask register retaining the other bits.

     Parameter  Description
  1  $index     Variable containing bit position to clear
  2  $mask      Mask register

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::setBit($index, $mask)

Set a bit in the specified register retaining the other bits.

     Parameter  Description
  1  $index     Variable containing bit position to set
  2  $mask      Mask register

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::clearBit($index, $mask)

Clear a bit in the specified mask register retaining the other bits.

     Parameter  Description
  1  $index     Variable containing bit position to clear
  2  $mask      Mask register

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::setZmm($source, $zmm, $offset, $length)

Load bytes from the memory addressed by specified source variable into the numbered zmm register at the offset in the specified offset moving the number of bytes in the specified variable.

     Parameter  Description
  1  $source    Variable containing the address of the source
  2  $zmm       Number of zmm to load
  3  $offset    Variable containing offset in zmm to move to
  4  $length    Variable containing length of move

B<Example:>


    my $s = Rb(0..128);
    my $source = V(Source=> $s);

    if (1)                                                                        # First block
     {$source->setZmm(0, K(key => 7), K length => 3);
     }

    if (1)                                                                        # Second block
     {$source->setZmm(0, K(key => 33), K key => 12);
     }

    PrintOutRegisterInHex zmm0;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: .... .... .... ...0  .... .... .... ...0 - .... ...B .A.9 .8.7  .6.5 .4.3 .2.1 .... + .... .... .... ...0  .... .... .... ...0 - .... .... .... .2.1  .... .... .... ...0
  END


=head3 Load mm registers

Load  zmm registers fom variables and retrieve data from zmm registers into variables.

=head4 Nasm::X86::Variable::loadZmm($source, $zmm)

Load bytes from the memory addressed by the specified source variable into the numbered zmm register.

     Parameter  Description
  1  $source    Variable containing the address of the source
  2  $zmm       Number of zmm to get

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head4 Nasm::X86::Variable::bFromZ($variable, $zmm, $offset)

Get the byte from the numbered zmm register and put it in a variable.

     Parameter  Description
  1  $variable  Variable
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::wFromZ($variable, $zmm, $offset)

Get the word from the numbered zmm register and put it in a variable.

     Parameter  Description
  1  $variable  Variable
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::dFromZ($variable, $zmm, $offset)

Get the double word from the numbered zmm register and put it in a variable.

     Parameter  Description
  1  $variable  Variable
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::qFromZ($variable, $zmm, $offset)

Get the quad word from the numbered zmm register and put it in a variable.

     Parameter  Description
  1  $variable  Variable
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::bIntoX($content, $xmm, $offset)

Place the value of the content variable at the byte in the numbered xmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $xmm       Numbered xmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::wIntoX($content, $xmm, $offset)

Place the value of the content variable at the word in the numbered xmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $xmm       Numbered xmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::dIntoX($content, $xmm, $offset)

Place the value of the content variable at the double word in the numbered xmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $xmm       Numbered xmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::qIntoX($content, $xmm, $offset)

Place the value of the content variable at the quad word in the numbered xmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $xmm       Numbered xmm
  3  $offset    Offset in bytes

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::bIntoZ($content, $zmm, $offset)

Place the value of the content variable at the byte in the numbered zmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    my $s = Rb(0..8);
    my $c = V("Content",   "[$s]");
       $c->bIntoZ     (0, 4);
       $c->putWIntoZmm(0, 6);
       $c->dIntoZ(0, 10);
       $c->qIntoZ(0, 16);
    PrintOutRegisterInHex zmm0;
    bFromZ(zmm0, 12)->outNL;
    wFromZ(zmm0, 12)->outNL;
    dFromZ(zmm0, 12)->outNL;
    qFromZ(zmm0, 12)->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .7.6 .5.4 .3.2 .1.. - .... .3.2 .1.. ....  .1.. .... .... ....
  b at offset 12 in zmm0: .... .... .... ...2
  w at offset 12 in zmm0: .... .... .... .3.2
  d at offset 12 in zmm0: .... .... .... .3.2
  q at offset 12 in zmm0: .3.2 .1.. .... .3.2
  END

    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::putWIntoZmm($content, $zmm, $offset)

Place the value of the content variable at the word in the numbered zmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    my $s = Rb(0..8);
    my $c = V("Content",   "[$s]");
       $c->bIntoZ     (0, 4);
       $c->putWIntoZmm(0, 6);
       $c->dIntoZ(0, 10);
       $c->qIntoZ(0, 16);
    PrintOutRegisterInHex zmm0;
    bFromZ(zmm0, 12)->outNL;
    wFromZ(zmm0, 12)->outNL;
    dFromZ(zmm0, 12)->outNL;
    qFromZ(zmm0, 12)->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .7.6 .5.4 .3.2 .1.. - .... .3.2 .1.. ....  .1.. .... .... ....
  b at offset 12 in zmm0: .... .... .... ...2
  w at offset 12 in zmm0: .... .... .... .3.2
  d at offset 12 in zmm0: .... .... .... .3.2
  q at offset 12 in zmm0: .3.2 .1.. .... .3.2
  END


=head4 Nasm::X86::Variable::dIntoZ($content, $zmm, $offset)

Place the value of the content variable at the double word in the numbered zmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    my $s = Rb(0..8);
    my $c = V("Content",   "[$s]");
       $c->bIntoZ     (0, 4);
       $c->putWIntoZmm(0, 6);
       $c->dIntoZ(0, 10);
       $c->qIntoZ(0, 16);
    PrintOutRegisterInHex zmm0;
    bFromZ(zmm0, 12)->outNL;
    wFromZ(zmm0, 12)->outNL;
    dFromZ(zmm0, 12)->outNL;
    qFromZ(zmm0, 12)->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .7.6 .5.4 .3.2 .1.. - .... .3.2 .1.. ....  .1.. .... .... ....
  b at offset 12 in zmm0: .... .... .... ...2
  w at offset 12 in zmm0: .... .... .... .3.2
  d at offset 12 in zmm0: .... .... .... .3.2
  q at offset 12 in zmm0: .3.2 .1.. .... .3.2
  END

    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head4 Nasm::X86::Variable::qIntoZ($content, $zmm, $offset)

Place the value of the content variable at the quad word in the numbered zmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $zmm       Numbered zmm
  3  $offset    Offset in bytes

B<Example:>


    my $s = Rb(0..8);
    my $c = V("Content",   "[$s]");
       $c->bIntoZ     (0, 4);
       $c->putWIntoZmm(0, 6);
       $c->dIntoZ(0, 10);
       $c->qIntoZ(0, 16);
    PrintOutRegisterInHex zmm0;
    bFromZ(zmm0, 12)->outNL;
    wFromZ(zmm0, 12)->outNL;
    dFromZ(zmm0, 12)->outNL;
    qFromZ(zmm0, 12)->outNL;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    zmm0: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .7.6 .5.4 .3.2 .1.. - .... .3.2 .1.. ....  .1.. .... .... ....
  b at offset 12 in zmm0: .... .... .... ...2
  w at offset 12 in zmm0: .... .... .... .3.2
  d at offset 12 in zmm0: .... .... .... .3.2
  q at offset 12 in zmm0: .3.2 .1.. .... .3.2
  END

    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head3 At a point

Place data into mm registers and retrieve data from them at the indicated point.

=head4 Nasm::X86::Variable::dFromPointInZ($point, $zmm, %options)

Get the double word from the numbered zmm register at a point specified by the variable and return it in a variable.

     Parameter  Description
  1  $point     Point
  2  $zmm       Numbered zmm
  3  %options   Options

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END

    my $tree = DescribeTree(length => 7);

    my $K = 31;

    K(K => Rd(0..15))->loadZmm($K);

    PrintOutRegisterInHex zmm $K;
    K( offset => 1 << 5)->dFromPointInZ($K)->outNL;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  d: .... .... .... ...5
  END


=head4 Nasm::X86::Variable::dIntoPointInZ($point, $zmm, $content)

Put the variable double word content into the numbered zmm register at a point specified by the variable.

     Parameter  Description
  1  $point     Point
  2  $zmm       Numbered zmm
  3  $content   Content to be inserted as a variable

B<Example:>


    Mov rax, 0x12345678;
    my $c = V("Content", rax);
    $c->bIntoX(1, 0);
    $c->bIntoX(1, 1);
    $c->wIntoX(1, 2);
    $c->dIntoX(1, 4);
    $c->qIntoX(1, 8);
    $c->bIntoZ(1, 16);
    $c->bIntoZ(1, 17);
    $c->dIntoZ(1, 20);
    $c->qIntoZ(1, 24);
    PrintOutRegisterInHex zmm1;

    my $q = V "var";

    $q->bFromZ(1, 16); $q->outNL;
    $q->bFromZ(1, 17); $q->outNL;
    $q->wFromZ(1, 18); $q->outNL;
    $q->dFromZ(1, 20); $q->outNL;
    $q->qFromZ(1, 20); $q->outNL;

    my $p = K key => 8;
    $p->dIntoPointInZ(1, K key => 0x22);
    $p->dFromPointInZ(1)->outNL;

    ok Assemble eq => <<END;
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
  var: .... .... .... ..78
  var: .... .... .... ..78
  var: .... .... .... ...0
  var: .... .... 1234 5678
  var: 1234 5678 1234 5678
  d: .... .... .... ..22
  END


=head2 Memory

Actions on memory described by variables

=head3 Nasm::X86::Variable::clearMemory($address, $size)

Clear the memory described in this variable.

     Parameter  Description
  1  $address   Address of memory to clear
  2  $size      Size of the memory to clear

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::copyMemory($target, $source, $size)

Copy from one block of memory to another.

     Parameter  Description
  1  $target    Address of target
  2  $source    Address of source
  3  $size      Length to copy

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::printOutMemory($address, $size)

Print the specified number of bytes of the memory addressed by the variable on stdout.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::printOutMemoryNL($address, $size)

Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head3 Nasm::X86::Variable::printOutMemoryInHexNL($address, $size)

Write the memory addressed by a variable to stdout.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

B<Example:>


    my $u = Rd(ord('𝝰'), ord('𝝱'), ord('𝝲'), ord('𝝳'));
    Mov rax, $u;
    my $address = V address=>rax;
    $address->printOutMemoryInHexNL(K size => 16);

    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
  70D7 .1.. 71D7 .1..  72D7 .1.. 73D7 .1..
  END

    my $v = V var => 2;

    If  $v == 0, Then {Mov rax, 0},
    Ef {$v == 1} Then {Mov rax, 1},
    Ef {$v == 2} Then {Mov rax, 2},
                 Else {Mov rax, 3};
    PrintOutRegisterInHex rax;
    ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
     rax: .... .... .... ...2
  END


=head3 Nasm::X86::Variable::freeMemory($address, $size)

Free the memory addressed by this variable for the specified length.

     Parameter  Description
  1  $address   Address of memory to free
  2  $size      Size of the memory to free

B<Example:>


    my $N = K size => 2048;
    my $q = Rs('a'..'p');
    my $address = $N->allocateMemory;

    Vmovdqu8 xmm0, "[$q]";
    $address->setReg(rax);
    Vmovdqu8 "[rax]", xmm0;
    Mov rdi, 16;
    PrintOutMemory;
    PrintOutNL;

    $address->freeMemory($N);

    ok Assemble eq => <<END;
  abcdefghijklmnop
  END


=head3 Nasm::X86::Variable::allocateMemory($size)

Allocate a variable amount of memory via mmap and return its address.

     Parameter  Description
  1  $size      Size as a variable

B<Example:>


    my $N = K size => 2048;
    my $q = Rs('a'..'p');
    my $address = $N->allocateMemory;

    Vmovdqu8 xmm0, "[$q]";
    $address->setReg(rax);
    Vmovdqu8 "[rax]", xmm0;
    Mov rdi, 16;
    PrintOutMemory;
    PrintOutNL;

    $address->freeMemory($N);

    ok Assemble eq => <<END;
  abcdefghijklmnop
  END


=head2 Structured Programming with variables

Structured programming operations driven off variables.

=head3 Nasm::X86::Variable::for($limit, $block)

Iterate a block a variable number of times.

     Parameter  Description
  1  $limit     Number of times
  2  $block     Block

B<Example:>


    V(limit => 10)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
     });

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  index: .... .... .... ...0
  index: .... .... .... ...1
  index: .... .... .... ...2
  index: .... .... .... ...3
  index: .... .... .... ...4
  index: .... .... .... ...5
  index: .... .... .... ...6
  index: .... .... .... ...7
  index: .... .... .... ...8
  index: .... .... .... ...9
  END


=head1 Operating system

Interacting with the operating system.

=head2 Processes

Create and manage processes

=head3 Fork()

Fork: create and execute a copy of the current process.


B<Example:>



    Fork;                                                                         # Fork  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Test rax,rax;
    IfNz                                                                          # Parent
    Then
     {Mov rbx, rax;
      WaitPid;
      GetPid;                                                                     # Pid of parent as seen in parent
      Mov rcx,rax;
      PrintOutRegisterInHex rax, rbx, rcx;
     },
    Else                                                                          # Child
     {Mov r8,rax;
      GetPid;                                                                     # Child pid as seen in child
      Mov r9,rax;
      GetPPid;                                                                    # Parent pid as seen in child
      Mov r10,rax;
      PrintOutRegisterInHex r8, r9, r10;
     };

    my $r = Assemble(avx512=>0);

  #    r8: 0000 0000 0000 0000   #1 Return from fork as seen by child
  #    r9: 0000 0000 0003 0C63   #2 Pid of child
  #   r10: 0000 0000 0003 0C60   #3 Pid of parent from child
  #   rax: 0000 0000 0003 0C63   #4 Return from fork as seen by parent
  #   rbx: 0000 0000 0003 0C63   #5 Wait for child pid result
  #   rcx: 0000 0000 0003 0C60   #6 Pid of parent

    if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
     {ok $2 eq $4;
      ok $2 eq $5;
      ok $3 eq $6;
      ok $2 gt $6;
     }


=head3 GetPid()

Get process identifier.


B<Example:>


    Fork;                                                                         # Fork

    Test rax,rax;
    IfNz                                                                          # Parent
    Then
     {Mov rbx, rax;
      WaitPid;

      GetPid;                                                                     # Pid of parent as seen in parent  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      Mov rcx,rax;
      PrintOutRegisterInHex rax, rbx, rcx;
     },
    Else                                                                          # Child
     {Mov r8,rax;

      GetPid;                                                                     # Child pid as seen in child  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      Mov r9,rax;
      GetPPid;                                                                    # Parent pid as seen in child
      Mov r10,rax;
      PrintOutRegisterInHex r8, r9, r10;
     };

    my $r = Assemble(avx512=>0);

  #    r8: 0000 0000 0000 0000   #1 Return from fork as seen by child
  #    r9: 0000 0000 0003 0C63   #2 Pid of child
  #   r10: 0000 0000 0003 0C60   #3 Pid of parent from child
  #   rax: 0000 0000 0003 0C63   #4 Return from fork as seen by parent
  #   rbx: 0000 0000 0003 0C63   #5 Wait for child pid result
  #   rcx: 0000 0000 0003 0C60   #6 Pid of parent

    if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
     {ok $2 eq $4;
      ok $2 eq $5;
      ok $3 eq $6;
      ok $2 gt $6;
     }


=head3 GetPidInHex()

Get process identifier in hex as 8 zero terminated bytes in rax.


B<Example:>



    GetPidInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r15, rax;


    GetPidInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Cmp r15, rax;
    IfEq Then {PrintOutStringNL "Same"}, Else {PrintOutStringNL "Diff"};

    ok Assemble eq => <<END, avx512=>0;
  Same
  END


=head3 GetPPid()

Get parent process identifier.


B<Example:>


    Fork;                                                                         # Fork

    Test rax,rax;
    IfNz                                                                          # Parent
    Then
     {Mov rbx, rax;
      WaitPid;
      GetPid;                                                                     # Pid of parent as seen in parent
      Mov rcx,rax;
      PrintOutRegisterInHex rax, rbx, rcx;
     },
    Else                                                                          # Child
     {Mov r8,rax;
      GetPid;                                                                     # Child pid as seen in child
      Mov r9,rax;

      GetPPid;                                                                    # Parent pid as seen in child  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      Mov r10,rax;
      PrintOutRegisterInHex r8, r9, r10;
     };

    my $r = Assemble(avx512=>0);

  #    r8: 0000 0000 0000 0000   #1 Return from fork as seen by child
  #    r9: 0000 0000 0003 0C63   #2 Pid of child
  #   r10: 0000 0000 0003 0C60   #3 Pid of parent from child
  #   rax: 0000 0000 0003 0C63   #4 Return from fork as seen by parent
  #   rbx: 0000 0000 0003 0C63   #5 Wait for child pid result
  #   rcx: 0000 0000 0003 0C60   #6 Pid of parent

    if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
     {ok $2 eq $4;
      ok $2 eq $5;
      ok $3 eq $6;
      ok $2 gt $6;
     }


=head3 GetUid()

Get userid of current process.


B<Example:>


  if (!onGitHub) {


=head3 WaitPid()

Wait for the pid in rax to complete.


B<Example:>


    Fork;                                                                         # Fork

    Test rax,rax;
    IfNz                                                                          # Parent
    Then
     {Mov rbx, rax;

      WaitPid;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      GetPid;                                                                     # Pid of parent as seen in parent
      Mov rcx,rax;
      PrintOutRegisterInHex rax, rbx, rcx;
     },
    Else                                                                          # Child
     {Mov r8,rax;
      GetPid;                                                                     # Child pid as seen in child
      Mov r9,rax;
      GetPPid;                                                                    # Parent pid as seen in child
      Mov r10,rax;
      PrintOutRegisterInHex r8, r9, r10;
     };

    my $r = Assemble(avx512=>0);

  #    r8: 0000 0000 0000 0000   #1 Return from fork as seen by child
  #    r9: 0000 0000 0003 0C63   #2 Pid of child
  #   r10: 0000 0000 0003 0C60   #3 Pid of parent from child
  #   rax: 0000 0000 0003 0C63   #4 Return from fork as seen by parent
  #   rbx: 0000 0000 0003 0C63   #5 Wait for child pid result
  #   rcx: 0000 0000 0003 0C60   #6 Pid of parent

    if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
     {ok $2 eq $4;
      ok $2 eq $5;
      ok $3 eq $6;
      ok $2 gt $6;
     }


=head3 ReadTimeStampCounter()

Read the time stamp counter and return the time in nanoseconds in rax.


B<Example:>


    for(1..10)

     {ReadTimeStampCounter;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      PrintOutRegisterInHex rax;
     }

    my @s = split /
/, Assemble(avx512=>0);
    my @S = sort @s;
    is_deeply \@s, \@S;


=head2 Memory

Allocate and print memory

=head3 PrintOutMemoryInHex()

Dump memory from the address in rax for the length in rdi on stdout.


B<Example:>


    Mov rax, 0x07654321;
    Shl rax, 32;
    Or  rax, 0x07654321;
    PushR rax;

    PrintOutRaxInHex;
    PrintOutNL;
    PrintOutRaxInReverseInHex;
    PrintOutNL;

    Mov rax, rsp;
    Mov rdi, 8;

    PrintOutMemoryInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;
    PopR rax;

    Mov rax, 4096;
    PushR rax;
    Mov rax, rsp;
    Mov rdi, 8;

    PrintOutMemoryInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;
    PopR rax;

    ok Assemble eq => <<END, avx512=>0;
  .765 4321 .765 4321
  2143 65.7 2143 65.7
  2143 65.7 2143 65.7
  ..10 .... .... ....
  END


=head3 PrintOutMemoryInHexNL()

Dump memory from the address in rax for the length in rdi and then print a new line.


B<Example:>


    my $N = 256;
    my $s = Rb 0..$N-1;
    my $a = AllocateMemory K size => $N;
    CopyMemory(V(source => $s), $a, K(size => $N));

    my $b = AllocateMemory K size => $N;
    CopyMemory($a, $b, K size => $N);

    $b->setReg(rax);
    Mov rdi, $N;
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END, avx512=>0;
  __.1 .2.3 .4.5 .6.7  .8.9 .A.B .C.D .E.F  1011 1213 1415 1617  1819 1A1B 1C1D 1E1F  2021 2223 2425 2627  2829 2A2B 2C2D 2E2F  3031 3233 3435 3637  3839 3A3B 3C3D 3E3F  4041 4243 4445 4647  4849 4A4B 4C4D 4E4F  5051 5253 5455 5657  5859 5A5B 5C5D 5E5F  6061 6263 6465 6667  6869 6A6B 6C6D 6E6F  7071 7273 7475 7677  7879 7A7B 7C7D 7E7F  8081 8283 8485 8687  8889 8A8B 8C8D 8E8F  9091 9293 9495 9697  9899 9A9B 9C9D 9E9F  A0A1 A2A3 A4A5 A6A7  A8A9 AAAB ACAD AEAF  B0B1 B2B3 B4B5 B6B7  B8B9 BABB BCBD BEBF  C0C1 C2C3 C4C5 C6C7  C8C9 CACB CCCD CECF  D0D1 D2D3 D4D5 D6D7  D8D9 DADB DCDD DEDF  E0E1 E2E3 E4E5 E6E7  E8E9 EAEB ECED EEEF  F0F1 F2F3 F4F5 F6F7  F8F9 FAFB FCFD FEFF
  END


=head3 PrintOutMemory_InHex()

Dump memory from the address in rax for the length in rdi on stdout.


B<Example:>


    K(loop => 8+1)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(15);
      Push r15;
     });

    Mov rax, rsp;
    Mov rdi, 8*9;
    PrintOutMemory_InHexNL;
    ClearMemory(V(address => rax), K(size => 8*9));

    PrintOutMemory_InHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutNL;

    ok Assemble eq => <<END;
  .8__ ____ ____ ____  .7__ ____ ____ ____  .6__ ____ ____ ____  .5__ ____ ____ ____  .4__ ____ ____ ____  .3__ ____ ____ ____  .2__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 PrintOutMemory_InHexNL()

Dump memory from the address in rax for the length in rdi and then print a new line.


B<Example:>


    K(loop => 8+1)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(15);
      Push r15;
     });

    Mov rax, rsp;
    Mov rdi, 8*9;

    PrintOutMemory_InHexNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ClearMemory(V(address => rax), K(size => 8*9));
    PrintOutMemory_InHex;
    PrintOutNL;

    ok Assemble eq => <<END;
  .8__ ____ ____ ____  .7__ ____ ____ ____  .6__ ____ ____ ____  .5__ ____ ____ ____  .4__ ____ ____ ____  .3__ ____ ____ ____  .2__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 PrintOutMemory()

Print the memory addressed by rax for a length of rdi on stdout.


B<Example:>


    Comment "Print a string from memory";
    my $s = "Hello World";
    Mov rax, Rs($s);
    Mov rdi, length $s;

    PrintOutMemory;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Exit(0);

    ok Assemble(avx512=>0) =~ m(Hello World);


=head3 PrintOutMemoryNL()

Print the memory addressed by rax for a length of rdi followed by a new line on stdout.


B<Example:>


    my $s = Rs("Hello World

Hello Skye");
    my $l = StringLength(my $t = V string => $s);
    $t->setReg(rax);
    $l->setReg(rdi);

    PrintOutMemoryNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END, avx512=>0;
  Hello World

  Hello Skye
  END


=head3 AllocateMemory($size)

Allocate the variable specified amount of memory via mmap and return its address as a variable.

     Parameter  Description
  1  $size      Size as a variable

B<Example:>


    my $N = K size => 2048;
    my $q = Rs('a'..'p');

    my $address = AllocateMemory $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Vmovdqu8 xmm0, "[$q]";
    $address->setReg(rax);
    Vmovdqu8 "[rax]", xmm0;
    Mov rdi, 16;
    PrintOutMemory;
    PrintOutNL;

    FreeMemory $address, $N;

    ok Assemble eq => <<END;
  abcdefghijklmnop
  END

    my $N = K size => 4096;                                                       # Size of the initial allocation which should be one or more pages


    my $A = AllocateMemory $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov rdi, 128;
    PrintOutMemory_InHexNL;

    FreeMemory $A, $N;

    ok Assemble eq => <<END;
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END

    my $N = 256;
    my $s = Rb 0..$N-1;

    my $a = AllocateMemory K size => $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    CopyMemory(V(source => $s), $a, K(size => $N));


    my $b = AllocateMemory K size => $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    CopyMemory($a, $b, K size => $N);

    $b->setReg(rax);
    Mov rdi, $N;
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END, avx512=>0;
  __.1 .2.3 .4.5 .6.7  .8.9 .A.B .C.D .E.F  1011 1213 1415 1617  1819 1A1B 1C1D 1E1F  2021 2223 2425 2627  2829 2A2B 2C2D 2E2F  3031 3233 3435 3637  3839 3A3B 3C3D 3E3F  4041 4243 4445 4647  4849 4A4B 4C4D 4E4F  5051 5253 5455 5657  5859 5A5B 5C5D 5E5F  6061 6263 6465 6667  6869 6A6B 6C6D 6E6F  7071 7273 7475 7677  7879 7A7B 7C7D 7E7F  8081 8283 8485 8687  8889 8A8B 8C8D 8E8F  9091 9293 9495 9697  9899 9A9B 9C9D 9E9F  A0A1 A2A3 A4A5 A6A7  A8A9 AAAB ACAD AEAF  B0B1 B2B3 B4B5 B6B7  B8B9 BABB BCBD BEBF  C0C1 C2C3 C4C5 C6C7  C8C9 CACB CCCD CECF  D0D1 D2D3 D4D5 D6D7  D8D9 DADB DCDD DEDF  E0E1 E2E3 E4E5 E6E7  E8E9 EAEB ECED EEEF  F0F1 F2F3 F4F5 F6F7  F8F9 FAFB FCFD FEFF
  END


=head3 FreeMemory($address, $size)

Free memory specified by variables.

     Parameter  Description
  1  $address   Variable address of memory
  2  $size      Variable size of memory

B<Example:>


    my $N = K size => 2048;
    my $q = Rs('a'..'p');
    my $address = AllocateMemory $N;

    Vmovdqu8 xmm0, "[$q]";
    $address->setReg(rax);
    Vmovdqu8 "[rax]", xmm0;
    Mov rdi, 16;
    PrintOutMemory;
    PrintOutNL;


    FreeMemory $address, $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
  abcdefghijklmnop
  END

    my $N = K size => 4096;                                                       # Size of the initial allocation which should be one or more pages

    my $A = AllocateMemory $N;

    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov rdi, 128;
    PrintOutMemory_InHexNL;


    FreeMemory $A, $N;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 ClearMemory($address, $size)

Clear memory with a variable address and variable length.

     Parameter  Description
  1  $address   Address of memory as a variable
  2  $size      Size of memory as a variable

B<Example:>


    K(loop => 8+1)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(15);
      Push r15;
     });

    Mov rax, rsp;
    Mov rdi, 8*9;
    PrintOutMemory_InHexNL;

    ClearMemory(V(address => rax), K(size => 8*9));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutMemory_InHex;
    PrintOutNL;

    ok Assemble eq => <<END;
  .8__ ____ ____ ____  .7__ ____ ____ ____  .6__ ____ ____ ____  .5__ ____ ____ ____  .4__ ____ ____ ____  .3__ ____ ____ ____  .2__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END

    my $N = K size => 4096;                                                       # Size of the initial allocation which should be one or more pages

    my $A = AllocateMemory $N;


    ClearMemory($A, $N);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    $A->setReg(rax);
    Mov rdi, 128;
    PrintOutMemory_InHexNL;

    FreeMemory $A, $N;

    ok Assemble eq => <<END;
  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 CopyMemory($source, $target, $size)

Copy memory.

     Parameter  Description
  1  $source    Source address variable
  2  $target    Target address variable
  3  $size      Length variable

B<Example:>


    my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;
    my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

    Vmovdqu8 xmm0, "[$s]";
    Vmovdqu8 xmm1, "[$t]";
    PrintOutRegisterInHex xmm0;
    PrintOutRegisterInHex xmm1;
    Sub rsp, 16;

    Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
    Mov rdi, 16;
    Mov rsi, $s;

    CopyMemory(V(source => rsi), V(target => rax), V size => rdi);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END;
    xmm0: .... .... .... ...4  .... ...3 ...2 .1..
    xmm1: .... .... .... ...4  .... ...3 ...2 .1..
  __.1 .2__ .3__ ____  .4__ ____ ____ ____
  END

    my $N = 256;
    my $s = Rb 0..$N-1;
    my $a = AllocateMemory K size => $N;

    CopyMemory(V(source => $s), $a, K(size => $N));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $b = AllocateMemory K size => $N;

    CopyMemory($a, $b, K size => $N);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    $b->setReg(rax);
    Mov rdi, $N;
    PrintOutMemory_InHexNL;

    ok Assemble eq => <<END, avx512=>0;
  __.1 .2.3 .4.5 .6.7  .8.9 .A.B .C.D .E.F  1011 1213 1415 1617  1819 1A1B 1C1D 1E1F  2021 2223 2425 2627  2829 2A2B 2C2D 2E2F  3031 3233 3435 3637  3839 3A3B 3C3D 3E3F  4041 4243 4445 4647  4849 4A4B 4C4D 4E4F  5051 5253 5455 5657  5859 5A5B 5C5D 5E5F  6061 6263 6465 6667  6869 6A6B 6C6D 6E6F  7071 7273 7475 7677  7879 7A7B 7C7D 7E7F  8081 8283 8485 8687  8889 8A8B 8C8D 8E8F  9091 9293 9495 9697  9899 9A9B 9C9D 9E9F  A0A1 A2A3 A4A5 A6A7  A8A9 AAAB ACAD AEAF  B0B1 B2B3 B4B5 B6B7  B8B9 BABB BCBD BEBF  C0C1 C2C3 C4C5 C6C7  C8C9 CACB CCCD CECF  D0D1 D2D3 D4D5 D6D7  D8D9 DADB DCDD DEDF  E0E1 E2E3 E4E5 E6E7  E8E9 EAEB ECED EEEF  F0F1 F2F3 F4F5 F6F7  F8F9 FAFB FCFD FEFF
  END


=head3 CopyMemory64($source, $target, $size)

Copy memory in 64 byte blocks.

     Parameter  Description
  1  $source    Source address variable
  2  $target    Target address variable
  3  $size      Number of 64 byte blocks to move

B<Example:>


    my ($s, $l) =


=head2 Files

Interact with the operating system via files.

=head3 OpenRead()

Open a file, whose name is addressed by rax, for read and return the file descriptor in rax.


B<Example:>


    Mov rax, Rs($0);                                                              # File to read

    OpenRead;                                                                     # Open file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;
    CloseFile;                                                                    # Close file
    PrintOutRegisterInHex rax;

    Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write
    OpenWrite;                                                                    # Open file
    CloseFile;                                                                    # Close file

    ok Assemble eq => <<END;
     rax: .... .... .... ...3
     rax: .... .... .... ...0
  END
    ok -e $f;                                                                     # Created file
    unlink $f;


=head3 OpenWrite()

Create the file named by the terminated string addressed by rax for write.  The file handle will be returned in rax.


B<Example:>


  if (1)
   {my $s = "zzzCreated.data";
    my $f = Rs $s;
    Mov rax, $f;

    OpenWrite;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r15, rax;
    Mov rax, $f;
    Mov rdi, length $s;
    PrintMemory r15;
    CloseFile;
    ok Assemble eq=><<END, avx512=>1;
  END
    ok -e $s;
    unlink $s;
   }

    Mov rax, Rs($0);                                                              # File to read
    OpenRead;                                                                     # Open file
    PrintOutRegisterInHex rax;
    CloseFile;                                                                    # Close file
    PrintOutRegisterInHex rax;

    Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write

    OpenWrite;                                                                    # Open file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    CloseFile;                                                                    # Close file

    ok Assemble eq => <<END;
     rax: .... .... .... ...3
     rax: .... .... .... ...0
  END
    ok -e $f;                                                                     # Created file
    unlink $f;


=head3 CloseFile()

Close the file whose descriptor is in rax.


B<Example:>


  if (1)
   {my $s = "zzzCreated.data";
    my $f = Rs $s;
    Mov rax, $f;
    OpenWrite;
    Mov r15, rax;
    Mov rax, $f;
    Mov rdi, length $s;
    PrintMemory r15;

    CloseFile;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ok Assemble eq=><<END, avx512=>1;
  END
    ok -e $s;
    unlink $s;
   }

    Mov rax, Rs($0);                                                              # File to read
    OpenRead;                                                                     # Open file
    PrintOutRegisterInHex rax;

    CloseFile;                                                                    # Close file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write
    OpenWrite;                                                                    # Open file

    CloseFile;                                                                    # Close file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END;
     rax: .... .... .... ...3
     rax: .... .... .... ...0
  END
    ok -e $f;                                                                     # Created file
    unlink $f;


=head3 StatSize()

Stat a file whose name is addressed by rax to get its size in rax.


B<Example:>


    Mov rax, Rs $0;                                                               # File to stat

    StatSize;                                                                     # Stat the file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    my $r = Assemble(avx512=>0) =~ s( ) ()gsr;
    if ($r =~ m(rax:([0-9a-f]{16}))is)                                            # Compare file size obtained with that from fileSize()
     {is_deeply $1, sprintf("%016X", fileSize($0));
     }


=head3 ReadChar()

Read a character from stdin and return it in rax else return -1 in rax if no character was read.


B<Example:>


    my $e = q(readChar);

    ForEver
     {my ($start, $end) = @_;

      ReadChar;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      Cmp rax, 0xa;
      Jle $end;
      PrintOutRaxAsChar;
      PrintOutRaxAsCharNL;
     };
    PrintOutNL;

    Assemble keep => $e;

    my $r = qx(echo "ABCDCBA" | ./$e);
    is_deeply $r, <<END;
  AA
  BB
  CC
  DD
  CC
  BB
  AA

  END
    unlink $e;


=head3 ReadLine()

Reads up to 8 characters followed by a terminating return and place them into rax.


B<Example:>


    my $e = q(readLine);
    my $f = writeTempFile("hello
world
");


    ReadLine;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRaxAsTextNL;

    ReadLine;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRaxAsTextNL;

    Assemble keep => $e;

    is_deeply scalar(qx(./$e < $f)), <<END;
  hello
  world
  END
    unlink $f;
  }

  #latest:
  if (1) {
    my $e = q(readInteger);
    my $f = writeTempFile("11
22
");

    ReadInteger;
    Shl rax, 1;
    PrintOutRaxInDecNL;
    ReadInteger;
    Shl rax, 1;
    PrintOutRaxInDecNL;

    Assemble keep => $e;

    is_deeply scalar(qx(./$e < $f)), <<END;
  22
  44
  END

    unlink $e, $f;


=head3 ReadInteger()

Reads an integer in decimal and returns it in rax.


B<Example:>


    my $e = q(readInteger);
    my $f = writeTempFile("11
22
");


    ReadInteger;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Shl rax, 1;
    PrintOutRaxInDecNL;

    ReadInteger;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Shl rax, 1;
    PrintOutRaxInDecNL;

    Assemble keep => $e;

    is_deeply scalar(qx(./$e < $f)), <<END;
  22
  44
  END

    unlink $e, $f;


=head3 ReadFile($File)

Read a file into memory.

     Parameter  Description
  1  $File      Variable addressing a zero terminated string naming the file to be read in by mapping it

B<Example:>


  if (!!onGitHub) {


=head3 executeFileViaBash($file)

Execute the file named in a variable.

     Parameter  Description
  1  $file      File variable

B<Example:>


  if (0 and !onGitHub) {                                                          # Execute the content of an area


=head3 unlinkFile($file)

Unlink the named file.

     Parameter  Description
  1  $file      File variable

B<Example:>


  if (0 and !onGitHub) {                                                          # Execute the content of an area


=head1 Hash functions

Hash functions

=head2 Hash()

Hash a string addressed by rax with length held in rdi and return the hash code in r15.


B<Example:>


  # Make hash accept parameters at:

    Mov rax, "[rbp+24]";                                                          # Address of string as parameter
    StringLength(V string => rax)->setReg(rdi);                                   # Length of string to hash

    Hash();                                                                       # Hash string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintOutRegisterInHex r15;

    my $e = Assemble keep => 'hash';                                              # Assemble to the specified file name
    say STDERR qx($e "");
    say STDERR qx($e "a");
    ok qx($e "")  =~ m(r15: 0000 3F80 0000 3F80);                                 # Test well known hashes
    ok qx($e "a") =~ m(r15: 0000 3F80 C000 45B2);


    if (0)                                                                        # Hash various strings  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

     {my %r; my %f; my $count = 0;
      my $N = RegisterSize zmm0;

      if (1)                                                                      # Fixed blocks
       {for my $l(qw(a ab abc abcd), 'a a', 'a  a')
         {for my $i(1..$N)
           {my $t = $l x $i;
            last if $N < length $t;
            my $s = substr($t.(' ' x $N), 0, $N);
            next if $f{$s}++;
            my $r = qx($e "$s");
            say STDERR "$count  $r";
            if ($r =~ m(^.*r15:\s*(.*)$)m)
             {push $r{$1}->@*, $s;
              ++$count;
             }
           }
         }
       }

      if (1)                                                                      # Variable blocks
       {for my $l(qw(a ab abc abcd), '', 'a a', 'a  a')
         {for my $i(1..$N)
           {my $t = $l x $i;
            next if $f{$t}++;
            my $r = qx($e "$t");
            say STDERR "$count  $r";
            if ($r =~ m(^.*r15:\s*(.*)$)m)
             {push $r{$1}->@*, $t;
              ++$count;
             }
           }
         }
       }
      for my $r(keys %r)
       {delete $r{$r} if $r{$r}->@* < 2;
       }

      say STDERR dump(\%r);
      say STDERR "Keys hashed: ", $count;
      confess "Duplicates : ",  scalar keys(%r);
     }

    unlink 'hash';


=head1 Unicode

Convert between utf8 and utf32

=head2 GetNextUtf8CharAsUtf32($in)

Get the next UTF-8 encoded character from the addressed memory and return it as a UTF-32 character as a variable along with the size of the input character and a variable indicating the success - 1 -  or failure  - 0 - of the operation.

     Parameter  Description
  1  $in        Address of utf8 character as a variable

B<Example:>


    my ($out, $size, $fail);

    my $Chars = Rb(0x24, 0xc2, 0xa2, 0xc9, 0x91, 0xE2, 0x82, 0xAC, 0xF0, 0x90, 0x8D, 0x88);
    my $chars = V(chars => $Chars);


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+0;                        # Dollar               UTF-8 Encoding: 0x24                UTF-32 Encoding: 0x00000024  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('out1 : ');
    $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+1;                        # Cents                UTF-8 Encoding: 0xC2 0xA2           UTF-32 Encoding: 0x000000a2  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('out2 : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+3;                        # Alpha                UTF-8 Encoding: 0xC9 0x91           UTF-32 Encoding: 0x00000251  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('out3 : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+5;                        # Euro                 UTF-8 Encoding: 0xE2 0x82 0xAC      UTF-32 Encoding: 0x000020AC  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('out4 : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+8;                        # Gothic Letter Hwair  UTF-8 Encoding  0xF0 0x90 0x8D 0x88 UTF-32 Encoding: 0x00010348  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('out5 : ');     $size->outNL(' size : ');

    my $statement = qq(𝖺
 𝑎𝑠𝑠𝑖𝑔𝑛 【【𝖻 𝐩𝐥𝐮𝐬 𝖼】】
AAAAAAAA);                        # A sample sentence to parse

    my $s = K(statement => Rutf8($statement));
    my $l = StringLength $s;

    my $address = AllocateMemory $l;                                              # Allocate enough memory for a copy of the string
    CopyMemory($s, $address, $l);


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('outA : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('outB : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+5;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('outC : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+30;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('outD : ');     $size->outNL(' size : ');


   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+35;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $out->out('outE : ');     $size->outNL(' size : ');

    $address->printOutMemoryInHexNL($l);

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  out1 : .... .... .... ..24 size : .... .... .... ...1
  out2 : .... .... .... ..A2 size : .... .... .... ...2
  out3 : .... .... .... .251 size : .... .... .... ...2
  out4 : .... .... .... 20AC size : .... .... .... ...3
  out5 : .... .... ...1 .348 size : .... .... .... ...4
  outA : .... .... ...1 D5BA size : .... .... .... ...4
  outB : .... .... .... ...A size : .... .... .... ...1
  outC : .... .... .... ..20 size : .... .... .... ...1
  outD : .... .... .... ..20 size : .... .... .... ...1
  outE : .... .... .... ..10 size : .... .... .... ...2
  F09D 96BA .A20 F09D  918E F09D 91A0 F09D  91A0 F09D 9196 F09D  9194 F09D 919B 20E3  8090 E380 90F0 9D96  BB20 F09D 90A9 F09D  90A5 F09D 90AE F09D  90AC 20F0 9D96 BCE3  8091 E380 91.A 4141  4141 4141 4141 ....
  END


=head2 ConvertUtf8ToUtf32($a8, $s8)

Convert an allocated string of utf8 to an allocated string of utf32 and return its address and length.

     Parameter  Description
  1  $a8        Utf8 string address variable
  2  $s8        Utf8 length variable

B<Example:>


    my ($out, $size, $fail);

    my $Chars = Rb(0x24, 0xc2, 0xa2, 0xc9, 0x91, 0xE2, 0x82, 0xAC, 0xF0, 0x90, 0x8D, 0x88);
    my $chars = V(chars => $Chars);

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+0;                        # Dollar               UTF-8 Encoding: 0x24                UTF-32 Encoding: 0x00000024
    $out->out('out1 : ');
    $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+1;                        # Cents                UTF-8 Encoding: 0xC2 0xA2           UTF-32 Encoding: 0x000000a2
    $out->out('out2 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+3;                        # Alpha                UTF-8 Encoding: 0xC9 0x91           UTF-32 Encoding: 0x00000251
    $out->out('out3 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+5;                        # Euro                 UTF-8 Encoding: 0xE2 0x82 0xAC      UTF-32 Encoding: 0x000020AC
    $out->out('out4 : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+8;                        # Gothic Letter Hwair  UTF-8 Encoding  0xF0 0x90 0x8D 0x88 UTF-32 Encoding: 0x00010348
    $out->out('out5 : ');     $size->outNL(' size : ');

    my $statement = qq(𝖺
 𝑎𝑠𝑠𝑖𝑔𝑛 【【𝖻 𝐩𝐥𝐮𝐬 𝖼】】
AAAAAAAA);                        # A sample sentence to parse

    my $s = K(statement => Rutf8($statement));
    my $l = StringLength $s;

    my $address = AllocateMemory $l;                                              # Allocate enough memory for a copy of the string
    CopyMemory($s, $address, $l);

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address;
    $out->out('outA : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+4;
    $out->out('outB : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+5;
    $out->out('outC : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+30;
    $out->out('outD : ');     $size->outNL(' size : ');

   ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+35;
    $out->out('outE : ');     $size->outNL(' size : ');

    $address->printOutMemoryInHexNL($l);

    ok Assemble(debug => 0, eq => <<END, avx512=>0);
  out1 : .... .... .... ..24 size : .... .... .... ...1
  out2 : .... .... .... ..A2 size : .... .... .... ...2
  out3 : .... .... .... .251 size : .... .... .... ...2
  out4 : .... .... .... 20AC size : .... .... .... ...3
  out5 : .... .... ...1 .348 size : .... .... .... ...4
  outA : .... .... ...1 D5BA size : .... .... .... ...4
  outB : .... .... .... ...A size : .... .... .... ...1
  outC : .... .... .... ..20 size : .... .... .... ...1
  outD : .... .... .... ..20 size : .... .... .... ...1
  outE : .... .... .... ..10 size : .... .... .... ...2
  F09D 96BA .A20 F09D  918E F09D 91A0 F09D  91A0 F09D 9196 F09D  9194 F09D 919B 20E3  8090 E380 90F0 9D96  BB20 F09D 90A9 F09D  90A5 F09D 90AE F09D  90AC 20F0 9D96 BCE3  8091 E380 91.A 4141  4141 4141 4141 ....
  END


=head1 C Strings

C strings are a series of bytes terminated by a zero byte.

=head2 StringLength($string)

Length of a zero terminated string.

     Parameter  Description
  1  $string    String

B<Example:>


    my $s = Rs("Hello World

Hello Skye");

    my $l = StringLength(my $t = V string => $s);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $t->setReg(rax);
    $l->setReg(rdi);
    PrintOutMemoryNL;

    ok Assemble eq => <<END, avx512=>0;
  Hello World

  Hello Skye
  END


    StringLength(V string => Rs("abcd"))->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Assemble eq => <<END, avx512=>0;
  size: .... .... .... ...4
  END


=head1 Areas

An area is single extensible block of memory which contains other data structures such as strings, arrays, trees within it.

=head2 Constructors

Construct an area either in memory or by reading it from a file or by incorporating it into an assembly.

=head3 CreateArea(%options)

Create an relocatable area and returns its address in rax. We add a chain header so that 64 byte blocks of memory can be freed and reused within the area.

     Parameter  Description
  1  %options   Free=>1 adds a free chain.

B<Example:>



    my $a = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->q('aa');
    $a->outNL;
    $a->ql('bb');
    $a->out;
    ok Assemble eq => <<END, avx512=>0;
  aa
  aabb
  END


    my $a = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $b = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->q('aa');
    $b->q('bb');
    $a->out;
    PrintOutNL;
    $b->out;
    PrintOutNL;
    ok Assemble eq => <<END, avx512=>0;
  aa
  bb
  END


    my $a = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $b = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->q('aa');
    $a->q('AA');
    $a->out;
    PrintOutNL;
    ok Assemble eq => <<END, avx512=>0;
  aaAA
  END


    my $a = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    my $b = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->q('aa');
    $b->q('bb');
    $a->q('AA');
    $b->q('BB');
    $a->q('aa');
    $b->q('bb');
    $a->out;
    $b->out;
    PrintOutNL;
    ok Assemble eq => <<END, avx512=>0;
  aaAAaabbBBbb
  END


    my $a = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $a->q('ab');

    my $b = CreateArea;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $b->append($a);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $b->append($a);
    $b->append($a);
    $b->append($a);


    $a->out;   PrintOutNL;
    $b->outNL;
    my $sa = $a->used; $sa->outNL;
    my $sb = $b->used; $sb->outNL;
    $a->clear;
    my $sA = $a->used; $sA->outNL;
    my $sB = $b->used; $sB->outNL;

    ok Assemble eq => <<END, avx512=>0;
  abababababababab
  ababababababababababababababababababababababababababababababababababababab
  area used up: .... .... .... ..10
  area used up: .... .... .... ..4A
  area used up: .... .... .... ...0
  area used up: .... .... .... ..4A
  END


=head3 ReadArea($file)

Read an area stored in a file into memory and return an area descriptor for the area so created.

     Parameter  Description
  1  $file      Name of file to read

B<Example:>


  if (1)
   {LoadZmm 0, 61..61+63;

    my $a = CreateArea;
    $a->appendZmm(0);
    $a->printOut(0x44, 2);
    $a->dump("AA");
    $a->write(my $f = "aaa.txt");


    my $A = ReadArea $f;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    $A->dump("BB");
    ok Assemble eq => <<END;
  AB
  AA
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  BB
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 loadAreaIntoAssembly($file)

Load an area into the current assembly and return a descriptor for it.

     Parameter  Description
  1  $file      File containing an area

B<Example:>


    unlink my $f = q(zzzArea.data);
    my $sub = "abcd";

    my $s = Subroutine
     {my ($p, $s, $sub) = @_;

      my $a = Subroutine
       {my ($p, $s, $sub) = @_;
        Mov rax, 0x111;
        PrintOutRegisterInHex rax;
       } name => 'a', parameters=>[qw(a)];

      my $b = Subroutine
       {my ($p, $s, $sub) = @_;
        $$p{a}->setReg(rax);
        PrintOutRegisterInHex rax;
       } name => 'b', parameters=>[qw(a)];

      PrintOutStringNL "abcd";
      $$p{a}->outNL;
     } name => $sub, parameters=>[qw(a)], export => $f;                           # Export the library

    my $t = Subroutine {} name => "t", parameters=>[qw(a)];

    my sub mapSubroutines                                                         # Create a string tree mapping subroutine names to subroutine numbers
     {my $n = CreateArea->CreateTree(stringTree=>1);
      $n->putKeyString(constantString("abcd"), K offset => 0);
      $n->putKeyString(constantString("a"),    K offset => 1);
      $n->putKeyString(constantString("b"),    K offset => 2);
      $n
     }

    if (1)                                                                        # Read a library and call the subroutines there-in
     {my $a = ReadArea $f;                                                        # Reload the area elsewhere
      my ($inter, $symbols) = $a->readLibraryHeader(mapSubroutines);              # Create a tree mapping the subroutine numbers to subroutine offsets

      $inter->find(K sub => 1);                                                   # Look up the offset of the first subroutine

      If $inter->found > 0,
      Then
       {$t->call(parameters=>{a => K key => 0x9999},                              # Call position independent code
                              override => $a->address + $inter->data);
       },
      Else
       {PrintOutStringNL "Unable to locate subroutine 'a'";
       };

      $inter->find(K sub => 2);                                                   # Look  up the offset of the second subroutine

      If $inter->found > 0,
      Then
       {$t->call(parameters=>{a        => K key => 0x9999},                       # Call position independent code
                              override => $a->address + $inter->data);
       },
      Else
       {PrintOutStringNL "Unable to locate subroutine 'b'";
       };
     }

    ok Assemble eq=><<END, avx512=>1;
     rax: .... .... .... .111
     rax: .... .... .... 9999
  END
    ok -e $f;                                                                     # Confirm we have created the library

    if (1)                                                                        # Include a library in a program

     {my $a = loadAreaIntoAssembly $f;                                            # Load the library from the file it was exported to  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

      my ($inter, $subroutines) = $a->readLibraryHeader(mapSubroutines);          # Create a tree mapping the subroutine numbers to subroutine offsets

      $inter->find(K sub => 0);                                                   # Look  up the offset of the containing subroutine
      $t->call(parameters=>{a        => K key => 0x6666},                         # Call position independent code
                            override => $a->address + $inter->data);

      $inter->find(K sub => 1);                                                   # Look  up the offset of the first subroutine
      $t->call(parameters=>{a        => K key => 0x7777},                         # Call position independent code
                            override => $a->address + $inter->data);

      $inter->find(K sub => 2);                                                   # Look  up the offset of the second subroutine
      $t->call(parameters=>{a        => K key => 0x8888},                         # Call position independent code
                            override => $a->address + $inter->data);
     }

    ok Assemble eq=><<END, avx512=>1;
  abcd
  a: .... .... .... 6666
     rax: .... .... .... .111
     rax: .... .... .... 8888
  END

    unlink $f;


=head3 Nasm::X86::Area::free($area)

Free an area.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;

    $a->q("a" x 255);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('A');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('B');

    $a->q("a" x 4095);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('C');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('D');

    $a->free;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
  area used up: .... .... .... ..FF
  size of area: .... .... .... 10..
  A
  Area     Size:     4096    Used:      319
  .... .... .... ...0 | __10 ____ ____ ____  3F.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 10..
  B
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... .FFF
  size of area: .... .... .... 20..
  C
  Area     Size:     8192    Used:     4159
  .... .... .... ...0 | __20 ____ ____ ____  3F10 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 20..
  D
  Area     Size:     8192    Used:       64
  .... .... .... ...0 | __20 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  END


=head2 Memory

Manage memory controlled by an area.

=head3 Nasm::X86::Area::used($area)

Return the currently used size of an area as a variable.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;

    $a->q("a" x 255);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('A');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('B');

    $a->q("a" x 4095);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('C');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('D');

    $a->free;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
  area used up: .... .... .... ..FF
  size of area: .... .... .... 10..
  A
  Area     Size:     4096    Used:      319
  .... .... .... ...0 | __10 ____ ____ ____  3F.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 10..
  B
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... .FFF
  size of area: .... .... .... 20..
  C
  Area     Size:     8192    Used:     4159
  .... .... .... ...0 | __20 ____ ____ ____  3F10 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 20..
  D
  Area     Size:     8192    Used:       64
  .... .... .... ...0 | __20 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  END


=head3 Nasm::X86::Area::size($area)

Get the size of an area as a variable.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;

    $a->q("a" x 255);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('A');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('B');

    $a->q("a" x 4095);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('C');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('D');

    $a->free;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
  area used up: .... .... .... ..FF
  size of area: .... .... .... 10..
  A
  Area     Size:     4096    Used:      319
  .... .... .... ...0 | __10 ____ ____ ____  3F.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 10..
  B
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... .FFF
  size of area: .... .... .... 20..
  C
  Area     Size:     8192    Used:     4159
  .... .... .... ...0 | __20 ____ ____ ____  3F10 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 20..
  D
  Area     Size:     8192    Used:       64
  .... .... .... ...0 | __20 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  END


=head3 Nasm::X86::Area::makeReadOnly($area)

Make an area read only.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $s = CreateArea;                                                           # Create an area
    $s->q("Hello");                                                               # Write data to area
    $s->makeReadOnly;                                                             # Make area read only - tested above
    $s->makeWriteable;                                                            # Make area writable again
    $s->q(" World");                                                              # Try to write to area
    $s->outNL;

    ok Assemble(avx512=>0, eq => <<END);
  Hello World
  END


=head3 Nasm::X86::Area::makeWriteable($area)

Make an area writable.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $s = CreateArea;                                                           # Create an area
    $s->q("Hello");                                                               # Write data to area
    $s->makeReadOnly;                                                             # Make area read only - tested above
    $s->makeWriteable;                                                            # Make area writable again
    $s->q(" World");                                                              # Try to write to area
    $s->outNL;

    ok Assemble(avx512=>0, eq => <<END);
  Hello World
  END


=head2 Alloc/Free

Allocate and free memory in an area, either once only but in variable size blocks or reusably in zmm sized blocks via the free block chain.

=head3 Nasm::X86::Area::allocate($area, $Size)

Allocate the variable amount of space in the variable addressed area and return the offset of the allocation in the area as a variable.

     Parameter  Description
  1  $area      Area descriptor
  2  $Size      Variable amount of allocation

B<Example:>


    my $s = CreateArea;
    my $o1 = $s->allocate(0x20);
    my $o2 = $s->allocate(0x30);
    my $o3 = $s->allocate(0x10);
    $o1->outNL;
    $o2->outNL;
    $o3->outNL;

    ok Assemble eq => <<END, avx512=>0;
  offset: .... .... .... ..40
  offset: .... .... .... ..60
  offset: .... .... .... ..90
  END


=head3 Nasm::X86::Area::allocZmmBlock($area)

Allocate a block to hold a zmm register in the specified area and return the offset of the block as a variable.

     Parameter  Description
  1  $area      Area

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END

    my $a = CreateArea;

    K(loop => 3)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
      my $m1 = $a->allocZmmBlock;
      my $m2 = $a->allocZmmBlock;

      K(K => Rd(1..16))->loadZmm(31);
      K(K => Rd(17..32))->loadZmm(30);
      PrintOutRegisterInHex 31, 30;

      $a->putZmmBlock($m1, 31);
      $a->putZmmBlock($m2, 30);
      $a->dump("A");

      $a->getZmmBlock($m1, 30);
      $a->getZmmBlock($m2, 31);
      PrintOutRegisterInHex 31, 30;

      $a->clearZmmBlock($m1);
      $a->freeZmmBlock($m1);
      $a->dump("B");

      $a->freeZmmBlock($m2);
      $a->dump("C");
     });

    ok Assemble eq => <<END, avx512=>1;
  index: .... .... .... ...0
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...1
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...2
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::freeZmmBlock($area, $offset)

Free a block in an area by placing it on the free chain.

     Parameter  Description
  1  $area      Area descriptor
  2  $offset    Offset of zmm block to be freed

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END

    my $a = CreateArea;

    K(loop => 3)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
      my $m1 = $a->allocZmmBlock;
      my $m2 = $a->allocZmmBlock;

      K(K => Rd(1..16))->loadZmm(31);
      K(K => Rd(17..32))->loadZmm(30);
      PrintOutRegisterInHex 31, 30;

      $a->putZmmBlock($m1, 31);
      $a->putZmmBlock($m2, 30);
      $a->dump("A");

      $a->getZmmBlock($m1, 30);
      $a->getZmmBlock($m2, 31);
      PrintOutRegisterInHex 31, 30;

      $a->clearZmmBlock($m1);
      $a->freeZmmBlock($m1);
      $a->dump("B");

      $a->freeZmmBlock($m2);
      $a->dump("C");
     });

    ok Assemble eq => <<END, avx512=>1;
  index: .... .... .... ...0
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...1
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...2
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::freeChainSpace($area)

Count the number of blocks available on the free chain.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->free;
    $a->used->out("Clear tree:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    $a->clear;
    $a->used->out("Clear area:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    ok Assemble eq => <<END, label=>'t5';
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear tree:            u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear area:            u: .... .... .... ...0 f: .... .... .... ...0 size of area: .... .... .... 10..
  END


=head3 Nasm::X86::Area::getZmmBlock($area, $block, $zmm)

Get the block with the specified offset in the specified string and return it in the numbered zmm.

     Parameter  Description
  1  $area      Area descriptor
  2  $block     Offset of the block as a variable or register
  3  $zmm       Number of zmm register to contain block

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END

    my $a = CreateArea;

    K(loop => 3)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
      my $m1 = $a->allocZmmBlock;
      my $m2 = $a->allocZmmBlock;

      K(K => Rd(1..16))->loadZmm(31);
      K(K => Rd(17..32))->loadZmm(30);
      PrintOutRegisterInHex 31, 30;

      $a->putZmmBlock($m1, 31);
      $a->putZmmBlock($m2, 30);
      $a->dump("A");

      $a->getZmmBlock($m1, 30);
      $a->getZmmBlock($m2, 31);
      PrintOutRegisterInHex 31, 30;

      $a->clearZmmBlock($m1);
      $a->freeZmmBlock($m1);
      $a->dump("B");

      $a->freeZmmBlock($m2);
      $a->dump("C");
     });

    ok Assemble eq => <<END, avx512=>1;
  index: .... .... .... ...0
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...1
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...2
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::putZmmBlock($area, $block, $zmm)

Write the numbered zmm to the block at the specified offset in the specified area.

     Parameter  Description
  1  $area      Area descriptor
  2  $block     Offset of the block as a variable
  3  $zmm       Number of zmm register to contain block

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END

    my $a = CreateArea;

    K(loop => 3)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
      my $m1 = $a->allocZmmBlock;
      my $m2 = $a->allocZmmBlock;

      K(K => Rd(1..16))->loadZmm(31);
      K(K => Rd(17..32))->loadZmm(30);
      PrintOutRegisterInHex 31, 30;

      $a->putZmmBlock($m1, 31);
      $a->putZmmBlock($m2, 30);
      $a->dump("A");

      $a->getZmmBlock($m1, 30);
      $a->getZmmBlock($m2, 31);
      PrintOutRegisterInHex 31, 30;

      $a->clearZmmBlock($m1);
      $a->freeZmmBlock($m1);
      $a->dump("B");

      $a->freeZmmBlock($m2);
      $a->dump("C");
     });

    ok Assemble eq => <<END, avx512=>1;
  index: .... .... .... ...0
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...1
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...2
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::clearZmmBlock($area, $offset)

Clear the zmm block at the specified offset in the area.

     Parameter  Description
  1  $area      Area descriptor
  2  $offset    Offset of the block as a variable

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Yggdrasil

The world tree from which we can address so many other things

=head3 Nasm::X86::Area::yggdrasil($area)

Return a tree descriptor to the Yggdrasil world tree for an area creating the world tree Yggdrasil if it has not already been created.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $A = CreateArea;
    my $t = $A->checkYggdrasilCreated;
       $t->found->outNL;
    my $y = $A->yggdrasil;
    my $T = $A->checkYggdrasilCreated;
       $T->found->outNL;
    ok Assemble debug => 0, eq => <<END, avx512=>1;
  found  : .... .... .... ...0
  found  : .... .... .... ...1
  END


=head2 Areas as Strings

Use the memory supplied by the area as a string - however, in general, this is too slow unless coupled with another slow operation such as executing a command, mapping a file or writing to a file.

=head3 Nasm::X86::Area::appendMemory($area, $address, $size)

Append the variable addressed content in memory of variable size to the specified area  and return its offset in that area. Pre-pack data as much as possible before using this routine to optimize processing.

     Parameter  Description
  1  $area      Area descriptor
  2  $address   Variable address of content
  3  $size      Variable length of content

B<Example:>


    my $a = CreateArea;
       $a->appendMemory(constantString("aaaa"));                                  # 82 48
       $a->dump("AA");

    ok Assemble eq => <<END, clocks=>12_391;
  AA
  Area     Size:     4096    Used:       68
  .... .... .... ...0 | __10 ____ ____ ____  44__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::appendZmm($area, $zmm)

Append the contents of the specified zmm to the specified area and returns its offset in that area as a variable,

     Parameter  Description
  1  $area      Area descriptor
  2  $zmm       Zmm number

B<Example:>


  if (1)
   {LoadZmm 0, 61..61+63;

    my $a = CreateArea;
    $a->appendZmm(0);
    $a->printOut(0x44, 2);
    $a->dump("AA");
    $a->write(my $f = "aaa.txt");

    my $A = ReadArea $f;
    $A->dump("BB");
    ok Assemble eq => <<END;
  AB
  AA
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  BB
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 Nasm::X86::Area::appendVar($area, $var)

Append the contents of a variable to the specified area

     Parameter  Description
  1  $area      Area descriptor
  2  $var       Variable

B<Example:>


  if (1)                                                                          # Place parser tables into an area
   {my $a = CreateArea;
    my ($alphabetN,    $alphabetA)    = Nasm::X86::Unisyn::Lex::AlphabetsArray;
    my ($transitionsN, $transitionsA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;

    $a->appendVar(V address => "[$transitionsN]");
    $a->appendVar(V address => "[$alphabetN]");                                   # Save sizes at start if area where they can be easily found

    $a->appendMemory(V(address => $transitionsA), V size => "[$transitionsN]");   # Save transitions
    $a->appendMemory(V(address => $alphabetA),    V size => "[$alphabetN]");      # Save alphabets classification

    $a->write("z123.txt");                                                        # Save the area to the named file
    ok Assemble eq => <<END;
  END
   }


=head3 Nasm::X86::Area::q($area, $string)

Append a constant string to the area.

     Parameter  Description
  1  $area      Area descriptor
  2  $string    String

B<Example:>


    my $a = CreateArea;
    $a->q('aa');
    $a->outNL;
    $a->ql('bb');
    $a->out;
    ok Assemble eq => <<END, avx512=>0;
  aa
  aabb
  END


=head3 Nasm::X86::Area::ql($area, $const)

Append a constant quoted string containing new line characters to the specified area.

     Parameter  Description
  1  $area      Area
  2  $const     Constant

B<Example:>


    my $a = CreateArea;
    $a->q('aa');
    $a->outNL;
    $a->ql('bb');
    $a->out;
    ok Assemble eq => <<END, avx512=>0;
  aa
  aabb
  END


=head3 Nasm::X86::Area::char($area, $char)

Append a constant character expressed as a decimal number to the specified area.

     Parameter  Description
  1  $area      Area descriptor
  2  $char      Number of character to be appended

B<Example:>


  if (1)
   {my $a = CreateArea;
    $a->char('a');
    $a->nl;
    $a->dump("AA");
    ok Assemble eq => <<END;
  AA
  Area     Size:     4096    Used:       66
  .... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }

  if (1)
   {my $a = CreateArea;
    $a->char('a');
    $a->nl;
    $a->dump("AA");
    ok Assemble eq => <<END;
  AA
  Area     Size:     4096    Used:       66
  .... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 Nasm::X86::Area::nl($area)

Append a new line to the area addressed by rax.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


  if (1)
   {my $a = CreateArea;
    $a->char('a');
    $a->nl;
    $a->dump("AA");
    ok Assemble eq => <<END;
  AA
  Area     Size:     4096    Used:       66
  .... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }

  if (1)
   {my $a = CreateArea;
    $a->char('a');
    $a->nl;
    $a->dump("AA");
    ok Assemble eq => <<END;
  AA
  Area     Size:     4096    Used:       66
  .... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 Nasm::X86::Area::append($target, $source)

Append one area to another.

     Parameter  Description
  1  $target    Target area descriptor
  2  $source    Source area descriptor

B<Example:>


    my $a = CreateArea;
    $a->q('ab');
    my $b = CreateArea;
    $b->append($a);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $b->append($a);
    $b->append($a);
    $b->append($a);


    $a->out;   PrintOutNL;
    $b->outNL;
    my $sa = $a->used; $sa->outNL;
    my $sb = $b->used; $sb->outNL;
    $a->clear;
    my $sA = $a->used; $sA->outNL;
    my $sB = $b->used; $sB->outNL;

    ok Assemble eq => <<END, avx512=>0;
  abababababababab
  ababababababababababababababababababababababababababababababababababababab
  area used up: .... .... .... ..10
  area used up: .... .... .... ..4A
  area used up: .... .... .... ...0
  area used up: .... .... .... ..4A
  END


=head3 Nasm::X86::Area::clear($area)

Clear an area but keep it at the same size.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;
    $a->q('ab');
    my $b = CreateArea;
    $b->append($a);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $b->append($a);
    $b->append($a);
    $b->append($a);


    $a->out;   PrintOutNL;
    $b->outNL;
    my $sa = $a->used; $sa->outNL;
    my $sb = $b->used; $sb->outNL;
    $a->clear;
    my $sA = $a->used; $sA->outNL;
    my $sB = $b->used; $sB->outNL;

    ok Assemble eq => <<END, avx512=>0;
  abababababababab
  ababababababababababababababababababababababababababababababababababababab
  area used up: .... .... .... ..10
  area used up: .... .... .... ..4A
  area used up: .... .... .... ...0
  area used up: .... .... .... ..4A
  END

    my $a = CreateArea;

    $a->q("a" x 255);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('A');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('B');

    $a->q("a" x 4095);
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('C');
    $a->clear;
    $a->used->outNL;
    $a->size->outNL;
    $a->dump('D');

    $a->free;

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
  area used up: .... .... .... ..FF
  size of area: .... .... .... 10..
  A
  Area     Size:     4096    Used:      319
  .... .... .... ...0 | __10 ____ ____ ____  3F.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 10..
  B
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... .FFF
  size of area: .... .... .... 20..
  C
  Area     Size:     8192    Used:     4159
  .... .... .... ...0 | __20 ____ ____ ____  3F10 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  area used up: .... .... .... ...0
  size of area: .... .... .... 20..
  D
  Area     Size:     8192    Used:       64
  .... .... .... ...0 | __20 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  .... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->free;
    $a->used->out("Clear tree:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    $a->clear;
    $a->used->out("Clear area:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    ok Assemble eq => <<END, label=>'t5';
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear tree:            u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear area:            u: .... .... .... ...0 f: .... .... .... ...0 size of area: .... .... .... 10..
  END


=head3 Nasm::X86::Area::read($area, $file)

Read a file specified by a variable addressed zero terminated string and append its content to the specified area.

     Parameter  Description
  1  $area      Area descriptor
  2  $file      Variable addressing file name

B<Example:>


    my $a = CreateArea;
    my $f = owf q(zzzArea.txt), "AA
";
    $a->read(V file => Rs $f);
    $a->dump("AA");

    ok Assemble(debug => 0, eq => <<END, avx512=>1);
  AA
  Area     Size:     4096    Used:       67
  .... .... .... ...0 | __10 ____ ____ ____  43__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 4141 .A__ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head3 Nasm::X86::Area::write($area, $file)

Write the content of the specified area to a file specified by a zero terminated string.

     Parameter  Description
  1  $area      Area descriptor
  2  $file      Variable addressing zero terminated file name

B<Example:>


  if (1)
   {LoadZmm 0, 61..61+63;

    my $a = CreateArea;
    $a->appendZmm(0);
    $a->printOut(0x44, 2);
    $a->dump("AA");
    $a->write(my $f = "aaa.txt");

    my $A = ReadArea $f;
    $A->dump("BB");
    ok Assemble eq => <<END;
  AB
  AA
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  BB
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 Nasm::X86::Area::out($area)

Print the specified area on sysout.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;
    $a->q('aa');
    $a->outNL;
    $a->ql('bb');
    $a->out;
    ok Assemble eq => <<END, avx512=>0;
  aa
  aabb
  END

    my $a = CreateArea;
    $a->q('ab');
    my $b = CreateArea;
    $b->append($a);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $b->append($a);
    $b->append($a);
    $b->append($a);


    $a->out;   PrintOutNL;
    $b->outNL;
    my $sa = $a->used; $sa->outNL;
    my $sb = $b->used; $sb->outNL;
    $a->clear;
    my $sA = $a->used; $sA->outNL;
    my $sB = $b->used; $sB->outNL;

    ok Assemble eq => <<END, avx512=>0;
  abababababababab
  ababababababababababababababababababababababababababababababababababababab
  area used up: .... .... .... ..10
  area used up: .... .... .... ..4A
  area used up: .... .... .... ...0
  area used up: .... .... .... ..4A
  END


=head3 Nasm::X86::Area::outNL($area)

Print the specified area on sysout followed by a new line.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea;
    $a->q('aa');
    $a->outNL;
    $a->ql('bb');
    $a->out;
    ok Assemble eq => <<END, avx512=>0;
  aa
  aabb
  END

    my $a = CreateArea;
    $a->q('ab');
    my $b = CreateArea;
    $b->append($a);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $a->append($b);
    $b->append($a);
    $b->append($a);
    $b->append($a);
    $b->append($a);


    $a->out;   PrintOutNL;
    $b->outNL;
    my $sa = $a->used; $sa->outNL;
    my $sb = $b->used; $sb->outNL;
    $a->clear;
    my $sA = $a->used; $sA->outNL;
    my $sB = $b->used; $sB->outNL;

    ok Assemble eq => <<END, avx512=>0;
  abababababababab
  ababababababababababababababababababababababababababababababababababababab
  area used up: .... .... .... ..10
  area used up: .... .... .... ..4A
  area used up: .... .... .... ...0
  area used up: .... .... .... ..4A
  END


=head3 Nasm::X86::Area::printOut($area, $Offset, $Length)

Print part of the specified area on sysout.

     Parameter  Description
  1  $area      Area descriptor
  2  $Offset    Offset
  3  $Length    Length

B<Example:>


  if (1)
   {LoadZmm 0, 61..61+63;

    my $a = CreateArea;
    $a->appendZmm(0);
    $a->printOut(0x44, 2);
    $a->dump("AA");
    $a->write(my $f = "aaa.txt");

    my $A = ReadArea $f;
    $A->dump("BB");
    ok Assemble eq => <<END;
  AB
  AA
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  BB
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END
   }


=head3 Nasm::X86::Area::dump($area, $title, $depth)

Dump details of an area.

     Parameter  Description
  1  $area      Area descriptor
  2  $title     Title string
  3  $depth     Optional variable number of 64 byte blocks to dump

B<Example:>


    my $a = CreateArea;

    my $m = $a->allocZmmBlock;
    K(K => Rd(1..16))->loadZmm(31);

    $a->putZmmBlock  ($m, 31);
    $a->dump("A");

    $a->getZmmBlock  ($m, 30);
    $a->clearZmmBlock($m);
    $a->getZmmBlock  ($m, 29);

    $a->clearZmmBlock($m);
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  A
  Area     Size:     4096    Used:      128
  .... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END

    my $a = CreateArea;

    K(loop => 3)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->outNL;
      my $m1 = $a->allocZmmBlock;
      my $m2 = $a->allocZmmBlock;

      K(K => Rd(1..16))->loadZmm(31);
      K(K => Rd(17..32))->loadZmm(30);
      PrintOutRegisterInHex 31, 30;

      $a->putZmmBlock($m1, 31);
      $a->putZmmBlock($m2, 30);
      $a->dump("A");

      $a->getZmmBlock($m1, 30);
      $a->getZmmBlock($m2, 31);
      PrintOutRegisterInHex 31, 30;

      $a->clearZmmBlock($m1);
      $a->freeZmmBlock($m1);
      $a->dump("B");

      $a->freeZmmBlock($m2);
      $a->dump("C");
     });

    ok Assemble eq => <<END, avx512=>1;
  index: .... .... .... ...0
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...1
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  index: .... .... .... ...2
   zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
  A
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  B
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  C
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  END


=head2 Areas as Stacks

Use an area as a stack. If the area is simultaneouls used for other operations confusion will ensue.

=head3 Nasm::X86::Area::push($area, $var)

Push the contents of a variable into an area

     Parameter  Description
  1  $area      Area descriptor
  2  $var       Variable

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::stackSize($area)

Size of the stack in an area being used as a stack

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::peek($area, $back)

Peek at a variable on the stack

     Parameter  Description
  1  $area      Area descriptor
  2  $back      How far back to look in the stack with the top most element being at one.

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::stackVariable($area, $index)

Peek at the variable indexed variable on the stack

     Parameter  Description
  1  $area      Area descriptor
  2  $index     Index of element sought

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::stackVariableSize($area)

Size of a stack of variables in an area.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::pop($area)

Pop a variable from the stack in an area being used as a stack

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::pushZmm($area, $zmm)

Push the contents of a zmm register into an area.

     Parameter  Description
  1  $area      Area descriptor
  2  $zmm       Zmm number

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::peekZmm($area, $zmm, $back)

Peek at a zmm register from the stack in an area being used as a stack and return a variable containing its offset in the area so we can update the pushed zmm if desired.

     Parameter  Description
  1  $area      Area descriptor
  2  $zmm       Zmm number
  3  $back      How far back to look in the stack with the top most element being at one.

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head3 Nasm::X86::Area::popZmm($area, $zmm)

Pop a zmm register from the stack in an area being used as a stack

     Parameter  Description
  1  $area      Area descriptor
  2  $zmm       Zmm number

B<Example:>


    my $a = CreateArea(stack=>1);

    $a->stackVariableSize->outNL;
    $a->dump("AA");
    $a->push(K key => 0x11111111);
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    $a->push(K key => 0x22222222);                                                # 12
    $a->stackVariableSize->outNL;
    $a->dump("AA");

    PrintOutStringNL "Peek";
    $a->peek(1)->outNL;
    $a->stackVariableSize->outNL;
    $a->peek(2)->outNL;
    $a->stackVariableSize->outNL;
    $a->stackSize->outNL;

    PrintOutStringNL "stackVariable";
    $a->stackVariable(K key => 0)->outNL;
    $a->stackVariable(K key => 1)->outNL;

    PrintOutStringNL "Pop";
    my $s1 = $a->pop;                                                             #  9
       $s1->outNL;
    $a->stackVariableSize->outNL;
    my $s2 = $a->pop;
       $s2->outNL;
    $a->stackVariableSize->outNL;
    $a->dump("pop1");
    $a->dump("pop2");

    ClearRegisters zmm1, zmm2;

    K(key => 0x22222222)->qIntoZ(1, 16);
    K(key => 0x33333333)->qIntoZ(1, 32);
    K(key => 0x44444444)->qIntoZ(1, 48);

    K(key => 0x55555555)->qIntoZ(2, 16);
    K(key => 0x66666666)->qIntoZ(2, 32);
    K(key => 0x77777777)->qIntoZ(2, 48);

    PrintOutStringNL "Push";
    $a->pushZmm(1)->outNL;
    $a->pushZmm(2)->outNL;

    $a->dump("zmm 1, 2");
    $a->peekZmm(1, 1)->outNL;
    $a->peekZmm(2, 2)->outNL;
    PrintOutStringNL "Peek";
    PrintOutRegisterInHex zmm1, zmm2;

    $a->popZmm(3);
    $a->popZmm(4);
    PrintOutStringNL "Pop";
    PrintOutRegisterInHex zmm3, zmm4;

    ok Assemble eq => <<END, clocks=>391;
  size: .... .... .... ...0
  AA
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...1
  AA
  Area     Size:     4096    Used:       72
  .... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  size: .... .... .... ...2
  AA
  Area     Size:     4096    Used:       80
  .... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Peek
  pop: .... .... 2222 2222
  size: .... .... .... ...2
  pop: .... .... 1111 1111
  size: .... .... .... ...2
  stackSize: .... .... .... ..10
  stackVariable
  pop: .... .... 1111 1111
  pop: .... .... 2222 2222
  Pop
  pop: .... .... 2222 2222
  size: .... .... .... ...1
  pop: .... .... 1111 1111
  size: .... .... .... ...0
  pop1
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  pop2
  Area     Size:     4096    Used:       64
  .... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  Push
  zmmStackOffset: .... .... .... ..40
  zmmStackOffset: .... .... .... ..80
  zmm 1, 2
  Area     Size:     4096    Used:      192
  .... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  offset: .... .... .... ..80
  offset: .... .... .... ..40
  Peek
    zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  Pop
    zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
    zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
  END


=head1 Tree

Tree constructed as sets of blocks in an area.

=head2 Constructors

Construct a tree.

=head3 Nasm::X86::Area::CreateTree($area, %options)

Create a tree in an area.

     Parameter  Description
  1  $area      Area description
  2  %options   Tree options

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put (K(key => 2), K(data => 0x22));
    $t->find(K key => 2);
    $t->found->outNL;
    $t->data ->outNL;

    ok Assemble eq => <<END, avx512=>1;
  found  : .... .... .... ...1
  data   : .... .... .... ..22
  END


=head3 Nasm::X86::Tree::cloneDescriptor($tree)

Clone the descriptor of a tree to make a new tree descriptor.

     Parameter  Description
  1  $tree      Tree descriptor

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->push(ord 'a');
    my $T = $t->cloneDescriptor;
    $T->push(ord 'b');
    my $c = $a->CreateTree;
    $c->copyDescriptor($T);
    $T->push(ord 'c');

    $t->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  abc
  END


=head3 Nasm::X86::Tree::copyDescriptor($target, $source)

Copy the description of one tree into another.

     Parameter  Description
  1  $target    The target of the copy
  2  $source    The source of the copy

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->push(ord 'a');
    my $T = $t->cloneDescriptor;
    $T->push(ord 'b');
    my $c = $a->CreateTree;
    $c->copyDescriptor($T);
    $T->push(ord 'c');

    $t->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  abc
  END


=head3 Nasm::X86::Tree::size($tree)

Return in a variable the number of elements currently in the tree.

     Parameter  Description
  1  $tree      Tree descriptor

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->put(K(key => 1), V(key => 1));  $t->size->outNL;
    $t->put(K(key => 2), K(key => 2));  $t->size->outNL;
    $t->put(V(key => 2), V(key => 2));  $t->size->outNL;

    $t->put(K(key => 3), K(key => 3));  $t->size->outNL;
    $t->put(V(key => 3), V(key => 3));  $t->size->outNL;

    $t->put(K(key => 4), K(key => 4));  $t->size->outNL;
    $t->put(K(key => 4), K(key => 4));  $t->size->outNL;

    $t->put(K(key => 5), K(key => 5));  $t->size->outNL;
    $t->put(V(key => 5), V(key => 5));  $t->size->outNL;

    $t->put(K(key => 6), K(key => 5));  $t->size->outNL;
    $t->put(V(key => 6), V(key => 5));  $t->size->outNL;

    $t->put(K(key => 7), K(key => 7));  $t->size->outNL;
    $t->put(V(key => 7), V(key => 7));  $t->size->outNL;

    $t->put(K(key => 8), K(key => 8));  $t->size->outNL;
    $t->put(V(key => 8), V(key => 8));  $t->size->outNL;

    $t->put(K(key => 9), K(key => 9));  $t->size->outNL;
    $t->put(V(key => 9), V(key => 9));  $t->size->outNL;

    $t->put(K(key => 10), K(key => 10));  $t->size->outNL;
    $t->put(V(key => 10), V(key => 10));  $t->size->outNL;

    $t->put(K(key => 11), K(key => 11));  $t->size->outNL;
    $t->put(V(key => 11), V(key => 11));  $t->size->outNL;

    $t->put(K(key => 12), K(key => 12));  $t->size->outNL;
    $t->put(V(key => 12), V(key => 12));  $t->size->outNL;

    $t->put(K(key => 13), K(key => 13));  $t->size->outNL;
    $t->put(V(key => 13), V(key => 13));  $t->size->outNL;

    $t->put(K(key => 14), K(key => 14));  $t->size->outNL;
    $t->put(V(key => 14), V(key => 14));  $t->size->outNL;

    $t->put(K(key => 15), K(key => 15));  $t->size->outNL;
    $t->put(V(key => 15), V(key => 15));  $t->size->outNL;

   $t->put(K(key => 4), K(key => 4));

   $t->dump8xx("AAA");

    ok Assemble debug => 0, eq => <<END, clocks=>18177;
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ...F
  AAA
  Tree: .... .... .... ..40
  At:      200                                                                                length:        1,  data:      240,  nodes:      280,  first:       40, root, parent
    Index:        0
    Keys :        7
    Data :        7
    Nodes:       80      140
      At:       80                                                                            length:        6,  data:       C0,  nodes:      100,  first:       40,  up:      200, leaf
        Index:        0        1        2        3        4        5
        Keys :        1        2        3        4        5        6
        Data :        1        2        3        4        5        5
      end
      At:      140                                                                            length:        8,  data:      180,  nodes:      1C0,  first:       40,  up:      200, leaf
        Index:        0        1        2        3        4        5        6        7
        Keys :        8        9        A        B        C        D        E        F
        Data :        8        9        A        B        C        D        E        F
      end
  end
  END


=head2 Insert

Insert a key into the tree.

=head3 Nasm::X86::Tree::put($tree, $key, $data)

Put a variable key and data into a tree. The data could be a tree descriptor to place a sub tree into a tree at the indicated key.

     Parameter  Description
  1  $tree      Tree definition
  2  $key       Variable key containing a number for a normal key or the offset in the area of a zmm block containing the key
  3  $data      Data as a variable or a tree descriptor

B<Example:>


  if (1)
   {my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put(1, 1);
    $t->find (1);
    $t->found->outNL;
    $t->data ->outNL;

    ok Assemble eq=><<END, avx512=>1;
  found  : .... .... .... ...1
  data   : .... .... .... ...1
  END
   }

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K count => 128;

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $l = $N-$index;
      $t->put($l, $l * 2);
      my $h = $N+$index;
      $t->put($h, $h * 2);
     });
    $t->put(K(zero=>0), K(zero=>0));
    $t->printInOrder("AAAA");

    PrintOutStringNL 'Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512';
    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $i = $index;
      my $j = $i * 2;
      my $k = $j * 2;
      my $l = $k * 2;
      my $m = $l * 2;
      my $n = $m * 2;
      my $o = $n * 2;
      my $p = $o * 2;
      my $q = $p * 2;
      $t->find($i); $i->outRightInDec(4); $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($j);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($k);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($l);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($m);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($n);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($o);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($p);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($q);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDecNL(8);
     });

     ok Assemble eq => <<END, avx512=>1;
  AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
  Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512
     0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0
     1      10      80       2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0
     2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0
     3    1000      80       6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0
     4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0
     5  100000      80      10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0
     6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0
     710000000      80      14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0
     800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0
     9       1     200      18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0
    10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0
    11      10    1DC0      22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0
    12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0
    13    1000    1DC0      26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0
    14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0
    15  100000    1DC0      30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0
    16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0
    17       1    1C40      34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0
    18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0
    19     100    1C40      38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0
    20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0
    21   10000    1C40      42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0
    22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0
    23     100     200      46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0
    24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0
    25      10    1AC0      50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0
    26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0
    27    1000    1AC0      54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0
    28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0
    29  100000    1AC0      5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0
    30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0
    31       1    1940      62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0
    32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0
    33     100    1940      66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0
    34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0
    35   10000    1940      70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0
    36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0
    37   10000     200      74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0
    38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0
    39      10    1640      78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0
    40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0
    41    1000    1640      82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0
    42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0
    43  100000    1640      86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0
    44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0
    45       1    14C0      90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0
    46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0
    47     100    14C0      94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0
    48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0
    49   10000    14C0      98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0
    50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0
    51 1000000     200     102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0
    52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0
    53      10    1340     106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0
    54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0
    55    1000    1340     110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0
    56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0
    57  100000    1340     114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0
    5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0
    59       1    11C0     118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0
    60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0
    61     100    11C0     122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0
    62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0
    63   10000    11C0     126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0
    64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    6500000000     200     130      10     140     260               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    67      10    1040     134  100000     140     268               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    69    1000    1040     138     100     380     276               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    71  100000    1040     142      10     BC0     284               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    73       1     EC0     146    1000     500     292               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    75     100     EC0     150       1     680     300               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    77   10000     EC0     154   10000     680     308               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    79       1     C80     158      10     800     316               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    81      10     D40     162  100000     800     324               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    83    1000     D40     166     100     980     332               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    85  100000     D40     170  100000     BC0     340               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    87       1     A40     174    1000     B00     348               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    89     100     A40     178       1     E00     356               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    91   10000     A40     182   10000     E00     364               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    93      10    1700     186      10     F80     372               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    95      10     8C0     190  100000     F80     380               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    97    1000     8C0     194     100    1100     388               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    99  100000     8C0     198     100    1880     396               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   101       1     740     202    1000    1280     404               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   103     100     740     206       1    1400     412               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   105   10000     740     210   10000    1400     420               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   107    1000    1700     214      10    1580     428               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   109      10     5C0     218  100000    1580     436               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   111    1000     5C0     222     100    17C0     444               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   113  100000     5C0     226 1000000    1880     452               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   115       1     440     230    1000    1A00     460               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   117     100     440     234       1    1B80     468               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   119   10000     440     238   10000    1B80     476               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   121  100000    1700     242      10    1D00     484               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   123      10     2C0     246  100000    1D00     492               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   125    1000     2C0     250     100    1E80     500               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   127  100000     2C0     254 1000000    1E80     508               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->put(K(key => 1), V(key => 1));  $t->size->outNL;
    $t->put(K(key => 2), K(key => 2));  $t->size->outNL;
    $t->put(V(key => 2), V(key => 2));  $t->size->outNL;

    $t->put(K(key => 3), K(key => 3));  $t->size->outNL;
    $t->put(V(key => 3), V(key => 3));  $t->size->outNL;

    $t->put(K(key => 4), K(key => 4));  $t->size->outNL;
    $t->put(K(key => 4), K(key => 4));  $t->size->outNL;

    $t->put(K(key => 5), K(key => 5));  $t->size->outNL;
    $t->put(V(key => 5), V(key => 5));  $t->size->outNL;

    $t->put(K(key => 6), K(key => 5));  $t->size->outNL;
    $t->put(V(key => 6), V(key => 5));  $t->size->outNL;

    $t->put(K(key => 7), K(key => 7));  $t->size->outNL;
    $t->put(V(key => 7), V(key => 7));  $t->size->outNL;

    $t->put(K(key => 8), K(key => 8));  $t->size->outNL;
    $t->put(V(key => 8), V(key => 8));  $t->size->outNL;

    $t->put(K(key => 9), K(key => 9));  $t->size->outNL;
    $t->put(V(key => 9), V(key => 9));  $t->size->outNL;

    $t->put(K(key => 10), K(key => 10));  $t->size->outNL;
    $t->put(V(key => 10), V(key => 10));  $t->size->outNL;

    $t->put(K(key => 11), K(key => 11));  $t->size->outNL;
    $t->put(V(key => 11), V(key => 11));  $t->size->outNL;

    $t->put(K(key => 12), K(key => 12));  $t->size->outNL;
    $t->put(V(key => 12), V(key => 12));  $t->size->outNL;

    $t->put(K(key => 13), K(key => 13));  $t->size->outNL;
    $t->put(V(key => 13), V(key => 13));  $t->size->outNL;

    $t->put(K(key => 14), K(key => 14));  $t->size->outNL;
    $t->put(V(key => 14), V(key => 14));  $t->size->outNL;

    $t->put(K(key => 15), K(key => 15));  $t->size->outNL;
    $t->put(V(key => 15), V(key => 15));  $t->size->outNL;

   $t->put(K(key => 4), K(key => 4));

   $t->dump8xx("AAA");

    ok Assemble debug => 0, eq => <<END, clocks=>18177;
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ...F
  AAA
  Tree: .... .... .... ..40
  At:      200                                                                                length:        1,  data:      240,  nodes:      280,  first:       40, root, parent
    Index:        0
    Keys :        7
    Data :        7
    Nodes:       80      140
      At:       80                                                                            length:        6,  data:       C0,  nodes:      100,  first:       40,  up:      200, leaf
        Index:        0        1        2        3        4        5
        Keys :        1        2        3        4        5        6
        Data :        1        2        3        4        5        5
      end
      At:      140                                                                            length:        8,  data:      180,  nodes:      1C0,  first:       40,  up:      200, leaf
        Index:        0        1        2        3        4        5        6        7
        Keys :        8        9        A        B        C        D        E        F
        Data :        8        9        A        B        C        D        E        F
      end
  end
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my $N = K(key => 999);
    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->put($index, $index);
     });

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->find($index);
     });

    ok Assemble eq => <<END, clocks=>763430;
  END


=head2 Find

Find a key in the tree. Trees have dword integer keys and so can act as arrays as well.

=head3 Nasm::X86::Tree::find($tree, $key)

Find a key in a tree and tests whether the found data is a sub tree.  The results are held in the variables "found", "data", "subTree" addressed by the tree descriptor. The key just searched for is held in the key field of the tree descriptor. The point at which it was found is held in B<found> which will be zero if the key was not found.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $key       Key field to search for which can either be a variable containing a double word for a normal tree or a zmm register containing the key to be sought for a string tree.

B<Example:>


  if (1)
   {my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put(1, 1);
    $t->find (1);
    $t->found->outNL;
    $t->data ->outNL;

    ok Assemble eq=><<END, avx512=>1;
  found  : .... .... .... ...1
  data   : .... .... .... ...1
  END
   }

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K count => 128;

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $l = $N-$index;
      $t->put($l, $l * 2);
      my $h = $N+$index;
      $t->put($h, $h * 2);
     });
    $t->put(K(zero=>0), K(zero=>0));
    $t->printInOrder("AAAA");

    PrintOutStringNL 'Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512';
    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $i = $index;
      my $j = $i * 2;
      my $k = $j * 2;
      my $l = $k * 2;
      my $m = $l * 2;
      my $n = $m * 2;
      my $o = $n * 2;
      my $p = $o * 2;
      my $q = $p * 2;
      $t->find($i); $i->outRightInDec(4); $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($j);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($k);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($l);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($m);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($n);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($o);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($p);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
      $t->find($q);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDecNL(8);
     });

     ok Assemble eq => <<END, avx512=>1;
  AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
  Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512
     0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0
     1      10      80       2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0
     2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0
     3    1000      80       6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0
     4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0
     5  100000      80      10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0
     6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0
     710000000      80      14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0
     800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0
     9       1     200      18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0
    10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0
    11      10    1DC0      22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0
    12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0
    13    1000    1DC0      26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0
    14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0
    15  100000    1DC0      30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0
    16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0
    17       1    1C40      34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0
    18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0
    19     100    1C40      38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0
    20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0
    21   10000    1C40      42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0
    22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0
    23     100     200      46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0
    24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0
    25      10    1AC0      50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0
    26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0
    27    1000    1AC0      54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0
    28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0
    29  100000    1AC0      5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0
    30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0
    31       1    1940      62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0
    32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0
    33     100    1940      66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0
    34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0
    35   10000    1940      70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0
    36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0
    37   10000     200      74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0
    38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0
    39      10    1640      78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0
    40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0
    41    1000    1640      82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0
    42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0
    43  100000    1640      86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0
    44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0
    45       1    14C0      90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0
    46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0
    47     100    14C0      94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0
    48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0
    49   10000    14C0      98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0
    50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0
    51 1000000     200     102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0
    52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0
    53      10    1340     106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0
    54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0
    55    1000    1340     110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0
    56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0
    57  100000    1340     114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0
    5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0
    59       1    11C0     118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0
    60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0
    61     100    11C0     122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0
    62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0
    63   10000    11C0     126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0
    64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    6500000000     200     130      10     140     260               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    67      10    1040     134  100000     140     268               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    69    1000    1040     138     100     380     276               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    71  100000    1040     142      10     BC0     284               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    73       1     EC0     146    1000     500     292               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    75     100     EC0     150       1     680     300               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    77   10000     EC0     154   10000     680     308               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    79       1     C80     158      10     800     316               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    81      10     D40     162  100000     800     324               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    83    1000     D40     166     100     980     332               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    85  100000     D40     170  100000     BC0     340               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    87       1     A40     174    1000     B00     348               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    89     100     A40     178       1     E00     356               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    91   10000     A40     182   10000     E00     364               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    93      10    1700     186      10     F80     372               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    95      10     8C0     190  100000     F80     380               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    97    1000     8C0     194     100    1100     388               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0               0       0
    99  100000     8C0     198     100    1880     396               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   101       1     740     202    1000    1280     404               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   103     100     740     206       1    1400     412               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   105   10000     740     210   10000    1400     420               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   107    1000    1700     214      10    1580     428               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   109      10     5C0     218  100000    1580     436               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   111    1000     5C0     222     100    17C0     444               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   113  100000     5C0     226 1000000    1880     452               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   115       1     440     230    1000    1A00     460               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   117     100     440     234       1    1B80     468               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   119   10000     440     238   10000    1B80     476               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   121  100000    1700     242      10    1D00     484               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   123      10     2C0     246  100000    1D00     492               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   125    1000     2C0     250     100    1E80     500               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0               0       0
   127  100000     2C0     254 1000000    1E80     508               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my $N = K(key => 999);
    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->put($index, $index);
     });

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->find($index);
     });

    ok Assemble eq => <<END, clocks=>763430;
  END


=head3 Nasm::X86::Tree::findFirst($tree)

Find the first element in a tree and set B<found>|B<key>|B<data>|B<subTree> to show the result.

     Parameter  Description
  1  $tree      Tree descriptor

B<Example:>


    my $N = K(key => 32);
    my $a = CreateArea;
    my $t = $a->CreateTree;

    $N->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->put($i, $i);
     });

    $N->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->put($N + $i, $N + $i);
      $t->findFirst;

      If $t->key != $i,
      Then
       {PrintOutTraceBack "Reverse queue first failed at: "; $i->outNL;
       };
      $t->delete($i);
      If $t->size != $N,
      Then
       {PrintOutTraceBack "Reverse queue size failed at: "; $i->outNL;
       };
      $t->printInOrder("A");
     });

    ok Assemble eq => <<END, avx512=>1;
  A  32:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20
  A  32:    2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21
  A  32:    3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22
  A  32:    4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  A  32:    5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24
  A  32:    6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25
  A  32:    7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26
  A  32:    8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27
  A  32:    9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28
  A  32:    A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29
  A  32:    B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A
  A  32:    C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B
  A  32:    D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C
  A  32:    E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D
  A  32:    F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E
  A  32:   10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F
  A  32:   11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30
  A  32:   12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31
  A  32:   13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32
  A  32:   14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33
  A  32:   15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34
  A  32:   16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35
  A  32:   17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36
  A  32:   18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37
  A  32:   19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38
  A  32:   1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39
  A  32:   1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A
  A  32:   1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B
  A  32:   1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C
  A  32:   1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D
  A  32:   1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E
  A  32:   20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F
  END


=head3 Nasm::X86::Tree::findLast($tree)

Find the last key in a tree - crucial for stack like operations.

     Parameter  Description
  1  $tree      Tree descriptor

B<Example:>


    my $N = K(key => 32);
    my $a = CreateArea;
    my $t = $a->CreateTree;

    $N->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->put($N + $i, $N + $i);
     });

    $N->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->put($N - $i, $N - $i);
      $t->findLast;
      $t->delete($t->key);
      If $t->size != $N - 1,
      Then
       {PrintOutTraceBack "Queued size failed at: "; $i->outNL;
       };
      $t->printInOrder("A");
     });

    ok Assemble eq => <<END, avx512=>1;
  A  31:   20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E
  A  31:   1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D
  A  31:   1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C
  A  31:   1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B
  A  31:   1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A
  A  31:   1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39
  A  31:   1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38
  A  31:   19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37
  A  31:   18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36
  A  31:   17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35
  A  31:   16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34
  A  31:   15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33
  A  31:   14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32
  A  31:   13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31
  A  31:   12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30
  A  31:   11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F
  A  31:   10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E
  A  31:    F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D
  A  31:    E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C
  A  31:    D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B
  A  31:    C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A
  A  31:    B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29
  A  31:    A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28
  A  31:    9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27
  A  31:    8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26
  A  31:    7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25
  A  31:    6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24
  A  31:    5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  A  31:    4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22
  A  31:    3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21
  A  31:    2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20
  A  31:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F
  END


=head3 Nasm::X86::Tree::findNext($tree, $key)

Find the next key greater than the one specified.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $key       Key

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 8;
    $N->for(sub
     {my ($i) = @_;
      $t->put(2*$i, 2*$i);
     });

    (2*$N)->for(sub
     {my ($i) = @_;
      $i->outRightInDec(4); PrintOutString " -> ";
      $t->findNext($i);
      $t->found->out("f: ", " ");
      If $t->found > 0, Then {$t->key->out};
      PrintOutStringNL '.';
     });

    ok Assemble eq => <<END, avx512=>1;
     0 -> f: .... .... .... ...2 key    : .... .... .... ...2.
     1 -> f: .... .... .... ...2 key    : .... .... .... ...2.
     2 -> f: .... .... .... ...4 key    : .... .... .... ...4.
     3 -> f: .... .... .... ...4 key    : .... .... .... ...4.
     4 -> f: .... .... .... ...8 key    : .... .... .... ...6.
     5 -> f: .... .... .... ...8 key    : .... .... .... ...6.
     6 -> f: .... .... .... ..10 key    : .... .... .... ...8.
     7 -> f: .... .... .... ..10 key    : .... .... .... ...8.
     8 -> f: .... .... .... ..20 key    : .... .... .... ...A.
     9 -> f: .... .... .... ..20 key    : .... .... .... ...A.
    10 -> f: .... .... .... ..40 key    : .... .... .... ...C.
    11 -> f: .... .... .... ..40 key    : .... .... .... ...C.
    12 -> f: .... .... .... ..80 key    : .... .... .... ...E.
    13 -> f: .... .... .... ..80 key    : .... .... .... ...E.
    14 -> f: .... .... .... ...0 .
    15 -> f: .... .... .... ...0 .
  END


=head3 Nasm::X86::Tree::findPrev($tree, $key)

Find the previous key less than the one specified.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $key       Key

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 8;
    $N->for(sub
     {my ($i) = @_;
      $t->put(2*$i, 2*$i);
     });

    (2*$N)->for(sub
     {my ($i) = @_;
      $i->outRightInDec(4); PrintOutString " -> ";
      $t->findPrev($i);
      $t->found->out("f: ", " ");
      If $t->found > 0, Then {$t->key->out};
      PrintOutStringNL '.';
     });

    ok Assemble eq => <<END, avx512=>1;
     0 -> f: .... .... .... ...0 .
     1 -> f: .... .... .... ...1 key    : .... .... .... ...0.
     2 -> f: .... .... .... ...1 key    : .... .... .... ...0.
     3 -> f: .... .... .... ...2 key    : .... .... .... ...2.
     4 -> f: .... .... .... ...2 key    : .... .... .... ...2.
     5 -> f: .... .... .... ...4 key    : .... .... .... ...4.
     6 -> f: .... .... .... ...4 key    : .... .... .... ...4.
     7 -> f: .... .... .... ...8 key    : .... .... .... ...6.
     8 -> f: .... .... .... ...8 key    : .... .... .... ...6.
     9 -> f: .... .... .... ..10 key    : .... .... .... ...8.
    10 -> f: .... .... .... ..10 key    : .... .... .... ...8.
    11 -> f: .... .... .... ..20 key    : .... .... .... ...A.
    12 -> f: .... .... .... ..20 key    : .... .... .... ...A.
    13 -> f: .... .... .... ..40 key    : .... .... .... ...C.
    14 -> f: .... .... .... ..40 key    : .... .... .... ...C.
    15 -> f: .... .... .... ..80 key    : .... .... .... ...E.
  END


=head3 Nasm::X86::Tree::findSubTree($tree, $key)

Find a key in the specified tree and create a sub tree from the data field if possible.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $key       Key as a dword

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;

    $t->put(1, $T);
    my $u = $t->findSubTree(1);
    $T->first->outNL;
    $u->first->outNL;

    ok Assemble eq => <<END, avx512=>1;
  first  : .... .... .... ..80
  first  : .... .... .... ..80
  END


=head2 Sub trees

Construct trees of trees - all private.

=head2 Delete

Delete a key from the tree

=head3 Nasm::X86::Tree::delete($tree, $key)

Find a key in a tree and delete it returning he value of the l=key deleted if found.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $key       Key field to delete

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->put   (K(key => 1), K(key => 0x11));
    $t->delete(K key => 1);
    $t->size->outNL;

    ok Assemble eq => <<END, avx512=>1;
  size of tree: .... .... .... ...0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    my $i1 = V  k => 1; $t->put($i1, $i1);
    $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
    $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  4
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    2    3
    Data :    1    2    3
  end
  X   3:    1   2   3
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i20 = V  k => 20; $t->put($i20, $i20);
    my $i30 = V  k => 30; $t->put($i30, $i30);
    my $i40 = V  k => 40; $t->put($i40, $i40);
    my $i10 = V  k => 10; $t->put($i10, $i10);
    my $i31 = V  k => 31; $t->put($i31, $i31);
    my $i32 = V  k => 32; $t->put($i32, $i32);
    my $i33 = V  k => 33; $t->put($i33, $i33);
    $t->size->outRightInDecNL(4);  $t->dump("33"); $t->delete($i33);
    $t->size->outRightInDecNL(4);  $t->dump("40"); $t->delete($i40);
    $t->size->outRightInDecNL(4);  $t->dump("X");  $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     7
  33
  At:   80                    length:    7,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4    5    6
    Keys :    A   14   1E   1F   20   21   28
    Data :   10   20   30   31   32   33   40
  end
     6
  40
  At:   80                    length:    6,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4    5
    Keys :    A   14   1E   1F   20   28
    Data :   10   20   30   31   32   40
  end
     5
  X
  At:   80                    length:    5,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4
    Keys :    A   14   1E   1F   20
    Data :   10   20   30   31   32
  end
  X   5:    A  14  1E  1F  20
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k =>  0; $t->put($i1, $i1);
    my $i2 = V  k => 11; $t->put($i2, $i2);
    my $i3 = V  k => 13; $t->put($i3, $i3);
    my $i4 = V  k => 15; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4);
    $t->dump("1");
    $a->dump("AAA", K blocks => 12);
    $t->delete($i2);
    $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  1
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    0    B    D    F
    Data :    0   11   13   15
  end
  AAA
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ .B__ ____  .D__ ____ .F__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0__ ____
  .... .... .... ..C0 | ____ ____ .B__ ____  .D__ ____ .F__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
  .... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
  .... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    0    D    F
    Data :    0   13   15
  end
  X   3:    0   D   F
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4);  $t->dump("1"); $a->dump("AAA", K blocks => 12); $t->delete($i1);
    $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  1
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
  AAA
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 80__ ____ ____ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0__ ____
  .... .... .... ..C0 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
  .... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
  .... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    2    3    4
    Data :    2    3    4
  end
  X   3:    2   3   4
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4); $t->dump("2"); $t->delete($i2);
    $t->size->outRightInDecNL(4); $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  2
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    3    4
    Data :    1    3    4
  end
  X   3:    1   3   4
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i3);
    $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  3
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    2    4
    Data :    1    2    4
  end
  X   3:    1   2   4
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
    $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

    ok Assemble eq => <<END, avx512=>1;
     4
  4
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
     3
  X
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    2    3
    Data :    1    2    3
  end
  X   3:    1   2   3
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    $t->size->outRightInDecNL(4);  $t->dump("0"); $t->delete($i2);
    $t->size->outRightInDecNL(4);  $t->dump("2"); $t->delete($i3);
    $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i4);
    $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i1);
    $t->size->outRightInDecNL(4);  $t->dump("1");

    ok Assemble eq => <<END, avx512=>1;
     4
  0
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :    1    2    3    4
  end
     3
  2
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    3    4
    Data :    1    3    4
  end
     2
  3
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    1    4
    Data :    1    4
  end
     1
  4
  At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0
    Keys :    1
    Data :    1
  end
     0
  1
  - empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put(   K(k=>1), K(d=>11));
    $t->put(   K(k=>2), K(d=>22));
    $t->put(   K(k=>3), K(d=>33));
    $t->delete(K k=>1);  $t->dump("1");
    $t->delete(K k=>3);  $t->dump("3");
    $t->delete(K k=>2);  $t->dump("2");
    ok Assemble eq => <<END, label=>'t4';
  1
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    2    3
    Data :   22   33
  end
  3
  At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0
    Keys :    2
    Data :   22
  end
  2
  - empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put(   K(k=>1), K(d=>11));
    $t->put(   K(k=>2), K(d=>22));
    $t->put(   K(k=>3), K(d=>33));
    $t->put(   K(k=>4), K(d=>44));
    $t->dump("0");
    $t->delete(K k=>1);
    $t->dump("1");
    $t->delete(K k=>2);
    $t->dump("2");
    $t->delete(K k=>3);
    $t->dump("3");
    $t->delete(K k=>4);
    $t->dump("4");
    ok Assemble eq => <<END, avx512=>1;
  0
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :   11   22   33   44
  end
  1
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    2    3    4
    Data :   22   33   44
  end
  2
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    3    4
    Data :   33   44
  end
  3
  At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0
    Keys :    4
    Data :   44
  end
  4
  - empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    $t->put(   K(k=>1), K(d=>11));
    $t->put(   K(k=>2), K(d=>22));
    $t->put(   K(k=>3), K(d=>33));
    $t->put(   K(k=>4), K(d=>44));
    $t->dump("0");
    $t->delete(K k=>3);
    $t->dump("3");
    $t->delete(K k=>4);
    $t->dump("4");
    $t->delete(K k=>2);
    $t->dump("2");
    $t->delete(K k=>1);
    $t->dump("1");
    ok Assemble eq => <<END, avx512=>1;
  0
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    1    2    3    4
    Data :   11   22   33   44
  end
  3
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    1    2    4
    Data :   11   22   44
  end
  4
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    1    2
    Data :   11   22
  end
  2
  At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0
    Keys :    1
    Data :   11
  end
  1
  - empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $i1 = V  k => 1; $t->put($i1, $i1);
    my $i2 = V  k => 2; $t->put($i2, $i2);
    my $i3 = V  k => 3; $t->put($i3, $i3);
    my $i4 = V  k => 4; $t->put($i4, $i4);
    my $i5 = V  k => 5; $t->put($i5, $i5);
    my $i6 = V  k => 6; $t->put($i6, $i6);
    my $i7 = V  k => 7; $t->put($i7, $i7);
    my $i8 = V  k => 8; $t->put($i8, $i8);
    $t->size->outRightInDecNL(4);  $t->dump("1"); $t->delete($i1);
    $t->size->outRightInDecNL(4);  $t->dump("2"); $t->delete($i2);
    $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i3);
    $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
    $t->size->outRightInDecNL(4);  $t->dump("5"); $t->delete($i5);
    $t->size->outRightInDecNL(4);  $t->dump("6"); $t->delete($i6);
    $t->size->outRightInDecNL(4);  $t->dump("7"); $t->delete($i7);
    $t->size->outRightInDecNL(4);  $t->dump("8"); $t->delete($i8);
    $t->size->outRightInDecNL(4);
    $t->dump("X");

    ok Assemble eq => <<END, avx512=>1;
     8
  1
  At:   80                    length:    8,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4    5    6    7
    Keys :    1    2    3    4    5    6    7    8
    Data :    1    2    3    4    5    6    7    8
  end
     7
  2
  At:   80                    length:    7,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4    5    6
    Keys :    2    3    4    5    6    7    8
    Data :    2    3    4    5    6    7    8
  end
     6
  3
  At:   80                    length:    6,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4    5
    Keys :    3    4    5    6    7    8
    Data :    3    4    5    6    7    8
  end
     5
  4
  At:   80                    length:    5,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3    4
    Keys :    4    5    6    7    8
    Data :    4    5    6    7    8
  end
     4
  5
  At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2    3
    Keys :    5    6    7    8
    Data :    5    6    7    8
  end
     3
  6
  At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1    2
    Keys :    6    7    8
    Data :    6    7    8
  end
     2
  7
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    7    8
    Data :    7    8
  end
     1
  8
  At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0
    Keys :    8
    Data :    8
  end
     0
  X
  - empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 20;
    $N->for(sub                                                                   # Load tree
     {my ($i) = @_;
      $t->put($i, $i);
     });
    $t->size->outNL;        $t->printInOrder("AA");
    $t->delete(K k =>  0);  $t->printInOrder(" 0");
    $t->delete(K k =>  9);  $t->printInOrder(" 9");
    $t->delete(K k =>  1);  $t->printInOrder(" 1");
    $t->delete(K k =>  8);  $t->printInOrder(" 8");
    $t->delete(K k =>  2);  $t->printInOrder(" 2");
    $t->delete(K k =>  7);  $t->printInOrder(" 7");
    $t->delete(K k =>  3);  $t->printInOrder(" 3");
    $t->delete(K k =>  6);  $t->printInOrder(" 6");
    $t->delete(K k =>  4);  $t->printInOrder(" 4");
    $t->delete(K k =>  5);  $t->printInOrder(" 5");
    $t->delete(K k => 10);  $t->printInOrder("10");
    $t->delete(K k => 19);  $t->printInOrder("19");
    $t->delete(K k => 11);  $t->printInOrder("11");
    $t->delete(K k => 18);  $t->printInOrder("18");
    $t->delete(K k => 12);  $t->printInOrder("12");
    $t->delete(K k => 17);  $t->printInOrder("17");
    $t->delete(K k => 13);  $t->printInOrder("13");
    $t->delete(K k => 16);  $t->printInOrder("16");
    $t->delete(K k => 14);  $t->printInOrder("14");
    $t->delete(K k => 15);  $t->printInOrder("15");

    ok Assemble eq => <<END, avx512=>1;
  size of tree: .... .... .... ..14
  AA  20:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13
   0  19:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13
   9  18:    1   2   3   4   5   6   7   8   A   B   C   D   E   F  10  11  12  13
   1  17:    2   3   4   5   6   7   8   A   B   C   D   E   F  10  11  12  13
   8  16:    2   3   4   5   6   7   A   B   C   D   E   F  10  11  12  13
   2  15:    3   4   5   6   7   A   B   C   D   E   F  10  11  12  13
   7  14:    3   4   5   6   A   B   C   D   E   F  10  11  12  13
   3  13:    4   5   6   A   B   C   D   E   F  10  11  12  13
   6  12:    4   5   A   B   C   D   E   F  10  11  12  13
   4  11:    5   A   B   C   D   E   F  10  11  12  13
   5  10:    A   B   C   D   E   F  10  11  12  13
  10   9:    B   C   D   E   F  10  11  12  13
  19   8:    B   C   D   E   F  10  11  12
  11   7:    C   D   E   F  10  11  12
  18   6:    C   D   E   F  10  11
  12   5:    D   E   F  10  11
  17   4:    D   E   F  10
  13   3:    E   F  10
  16   2:    E   F
  14   1:    F
  15- empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->put($i, $i);
     });
    $t->printInOrder(" 0"); $t->delete(K k =>  0);
    $t->printInOrder(" 2"); $t->delete(K k =>  2);
    $t->printInOrder(" 4"); $t->delete(K k =>  4);
    $t->printInOrder(" 6"); $t->delete(K k =>  6);
    $t->printInOrder(" 8"); $t->delete(K k =>  8);
    $t->printInOrder("10"); $t->delete(K k => 10);
    $t->printInOrder("12"); $t->delete(K k => 12);
    $t->printInOrder("14"); $t->delete(K k => 14);
    $t->printInOrder(" 1"); $t->delete(K k =>  1);
    $t->printInOrder(" 3"); $t->delete(K k =>  3);
    $t->printInOrder(" 5"); $t->delete(K k =>  5);
    $t->printInOrder(" 7"); $t->delete(K k =>  7);
    $t->printInOrder(" 9"); $t->delete(K k =>  9);
    $t->printInOrder("11"); $t->delete(K k => 11);
    $t->printInOrder("13"); $t->delete(K k => 13);
    $t->printInOrder("15"); $t->delete(K k => 15);
    $t->printInOrder("XX");

    ok Assemble eq => <<END, avx512=>1;
   0  16:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
   2  15:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
   4  14:    1   3   4   5   6   7   8   9   A   B   C   D   E   F
   6  13:    1   3   5   6   7   8   9   A   B   C   D   E   F
   8  12:    1   3   5   7   8   9   A   B   C   D   E   F
  10  11:    1   3   5   7   9   A   B   C   D   E   F
  12  10:    1   3   5   7   9   B   C   D   E   F
  14   9:    1   3   5   7   9   B   D   E   F
   1   8:    1   3   5   7   9   B   D   F
   3   7:    3   5   7   9   B   D   F
   5   6:    5   7   9   B   D   F
   7   5:    7   9   B   D   F
   9   4:    9   B   D   F
  11   3:    B   D   F
  13   2:    D   F
  15   1:    F
  XX- empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K max => 8;

    $N->for(sub                                                                   # Load tree
     {my ($i) = @_;
      $t->put(         $i,     2 *        $i);
      $t->put(2 * $N - $i - 1, 2 * ($N -  $i));
     });
  #  $t->printInOrder("Full");

    ($N-1)->for(sub                                                               # Delete elements
     {my ($i) = @_;
      my $n1 = ($N + $i)->clone("1111"); my $n2 = ($N - $i - 1)->clone("2222");

      $n1->outNL;
      $t->delete($n1);
      $t->printInOrder("1111");

      $n2->outNL;
      $t->delete($n2);
      $t->printInOrder("2222");
     });
    $t->dump("Two:");
    $t->size->outRightInDecNL(4);

    ok Assemble eq => <<END, avx512=>1;
  1111: .... .... .... ...8
  1111  15:    0   1   2   3   4   5   6   7   9   A   B   C   D   E   F
  2222: .... .... .... ...7
  2222  14:    0   1   2   3   4   5   6   9   A   B   C   D   E   F
  1111: .... .... .... ...9
  1111  13:    0   1   2   3   4   5   6   A   B   C   D   E   F
  2222: .... .... .... ...6
  2222  12:    0   1   2   3   4   5   A   B   C   D   E   F
  1111: .... .... .... ...A
  1111  11:    0   1   2   3   4   5   B   C   D   E   F
  2222: .... .... .... ...5
  2222  10:    0   1   2   3   4   B   C   D   E   F
  1111: .... .... .... ...B
  1111   9:    0   1   2   3   4   C   D   E   F
  2222: .... .... .... ...4
  2222   8:    0   1   2   3   C   D   E   F
  1111: .... .... .... ...C
  1111   7:    0   1   2   3   D   E   F
  2222: .... .... .... ...3
  2222   6:    0   1   2   D   E   F
  1111: .... .... .... ...D
  1111   5:    0   1   2   E   F
  2222: .... .... .... ...2
  2222   4:    0   1   E   F
  1111: .... .... .... ...E
  1111   3:    0   1   F
  2222: .... .... .... ...1
  2222   2:    0   F
  Two:
  At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
    Index:    0    1
    Keys :    0    F
    Data :    0   16
  end
     2
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K max => 100;

    $N->for(sub                                                                   # Load tree
     {my ($index, $start, $next, $end) = @_;
      $t->put($index, 2 * $index);
      If $t->size != $index + 1,
      Then
       {PrintOutStringNL "SSSS"; $index->outNL; Exit(0);
       };
     });

    $N->for(sub                                                                   # Check elements
     {my ($i) = @_;
      $t->find($i);
      If $t->found == 0,
      Then
       {PrintOutStringNL "AAAA"; $i->outNL; Exit(0);
       };
     });

    $N->for(sub                                                                   # Delete elements
     {my ($i) = @_;
      $t->delete($i);

      If $t->size != $N - $i - 1,
      Then
       {PrintOutStringNL "TTTT"; $i->outNL; Exit(0);
       };

      $N->for(sub                                                                 # Check elements
       {my ($j) = @_;
        $t->find($j);
        If $t->found == 0,
        Then
         {If $j > $i,
          Then
           {PrintOutStringNL "BBBBB"; $j->outNL; Exit(0);                         # Not deleted yet so it should be findable
           };
         },
        Else
         {If $j <= $i,
          Then
           {PrintOutStringNL "CCCCC"; $j->outNL; Exit(0);                         # Deleted so should not be findable
           };
         };
       });
     });

    ok Assemble eq => <<END, avx512=>1;
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->put($i, $i);
     });
    ($N/2)->for(sub
     {my ($i) = @_;
      $t->printInOrder("AAAA");
      $t->delete($i * 2);
     });
    ($N/2)->for(sub
     {my ($i) = @_;
      $t->printInOrder("BBBB");
      $t->delete($i * 2 + 1);
     });
    $t->printInOrder("CCCC");

    ok Assemble eq => <<END, avx512=>1;
  AAAA  16:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
  AAAA  15:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
  AAAA  14:    1   3   4   5   6   7   8   9   A   B   C   D   E   F
  AAAA  13:    1   3   5   6   7   8   9   A   B   C   D   E   F
  AAAA  12:    1   3   5   7   8   9   A   B   C   D   E   F
  AAAA  11:    1   3   5   7   9   A   B   C   D   E   F
  AAAA  10:    1   3   5   7   9   B   C   D   E   F
  AAAA   9:    1   3   5   7   9   B   D   E   F
  BBBB   8:    1   3   5   7   9   B   D   F
  BBBB   7:    3   5   7   9   B   D   F
  BBBB   6:    5   7   9   B   D   F
  BBBB   5:    7   9   B   D   F
  BBBB   4:    9   B   D   F
  BBBB   3:    B   D   F
  BBBB   2:    D   F
  BBBB   1:    F
  CCCC- empty
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 36;
    $N->for(sub
     {my ($i) = @_;
      $t->put($i, $i);
     });
    $t->delete(K 1 =>  0); $t->printInOrder(" 0");
    $t->delete(K 1 =>  5); $t->printInOrder(" 5");
    $t->delete(K 1 => 10); $t->printInOrder("10");
    $t->delete(K 1 => 15); $t->printInOrder("15");
    $t->delete(K 1 => 20); $t->printInOrder("20");
    $t->delete(K 1 => 25); $t->printInOrder("25");
    $t->delete(K 1 => 30); $t->printInOrder("30");
    $t->delete(K 1 => 35); $t->printInOrder("35");

    $t->delete(K 1 =>  1); $t->printInOrder(" 1");
    $t->delete(K 1 =>  6); $t->printInOrder(" 6");
    $t->delete(K 1 => 11); $t->printInOrder("11");
    $t->delete(K 1 => 16); $t->printInOrder("16");
    $t->delete(K 1 => 21); $t->printInOrder("21");
    $t->delete(K 1 => 26); $t->printInOrder("26");
    $t->delete(K 1 => 31); $t->printInOrder("31");

    $t->delete(K 1 =>  2); $t->printInOrder(" 2");
    $t->delete(K 1 =>  7); $t->printInOrder(" 7");
    $t->delete(K 1 => 12); $t->printInOrder("12");
    $t->delete(K 1 => 17); $t->printInOrder("17");
    $t->delete(K 1 => 22); $t->printInOrder("22");
    $t->delete(K 1 => 27); $t->printInOrder("27");
    $t->delete(K 1 => 32); $t->printInOrder("32");

    $t->delete(K 1 =>  3); $t->printInOrder(" 3");
    $t->delete(K 1 =>  8); $t->printInOrder(" 8");
    $t->delete(K 1 => 13); $t->printInOrder("13");
    $t->delete(K 1 => 18); $t->printInOrder("18");
    $t->delete(K 1 => 23); $t->printInOrder("23");
    $t->delete(K 1 => 28); $t->printInOrder("28");
    $t->delete(K 1 => 33); $t->printInOrder("33");

    $t->delete(K 1 =>  4); $t->printInOrder(" 4");
    $t->delete(K 1 =>  9); $t->printInOrder(" 9");
    $t->delete(K 1 => 14); $t->printInOrder("14");
    $t->delete(K 1 => 19); $t->printInOrder("19");
    $t->delete(K 1 => 24); $t->printInOrder("24");
    $t->delete(K 1 => 29); $t->printInOrder("29");
    $t->delete(K 1 => 34); $t->printInOrder("34");

    ok Assemble eq => <<END, avx512=>1;
   0  35:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
   5  34:    1   2   3   4   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  10  33:    1   2   3   4   6   7   8   9   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  15  32:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  20  31:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
  25  30:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1E  1F  20  21  22  23
  30  29:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22  23
  35  28:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
   1  27:    2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
   6  26:    2   3   4   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
  11  25:    2   3   4   7   8   9   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
  16  24:    2   3   4   7   8   9   C   D   E  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
  21  23:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1A  1B  1C  1D  1F  20  21  22
  26  22:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  1F  20  21  22
  31  21:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
   2  20:    3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
   7  19:    3   4   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
  12  18:    3   4   8   9   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
  17  17:    3   4   8   9   D   E  12  13  16  17  18  1B  1C  1D  20  21  22
  22  16:    3   4   8   9   D   E  12  13  17  18  1B  1C  1D  20  21  22
  27  15:    3   4   8   9   D   E  12  13  17  18  1C  1D  20  21  22
  32  14:    3   4   8   9   D   E  12  13  17  18  1C  1D  21  22
   3  13:    4   8   9   D   E  12  13  17  18  1C  1D  21  22
   8  12:    4   9   D   E  12  13  17  18  1C  1D  21  22
  13  11:    4   9   E  12  13  17  18  1C  1D  21  22
  18  10:    4   9   E  13  17  18  1C  1D  21  22
  23   9:    4   9   E  13  18  1C  1D  21  22
  28   8:    4   9   E  13  18  1D  21  22
  33   7:    4   9   E  13  18  1D  22
   4   6:    9   E  13  18  1D  22
   9   5:    E  13  18  1D  22
  14   4:   13  18  1D  22
  19   3:   18  1D  22
  24   2:   1D  22
  29   1:   22
  34- empty
  END


=head3 Nasm::X86::Tree::clear($tree)

Delete everything in the tree except the first block recording any memory liberated on the free chain.

     Parameter  Description
  1  $tree      Tree

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->free;
    $a->used->out("Clear tree:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    $a->clear;
    $a->used->out("Clear area:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    ok Assemble eq => <<END, label=>'t5';
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear tree:            u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear area:            u: .... .... .... ...0 f: .... .... .... ...0 size of area: .... .... .... 10..
  END


=head3 Nasm::X86::Tree::free($tree)

Free all the memory used by a tree.

     Parameter  Description
  1  $tree      Tree

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;
    $t->clear;
    $t->size->out("t: ", " ");
    $a->used->out("u: ", " ");
    $a->freeChainSpace->out("f: ", " ");
    $a->size->outNL;

    $N->for(sub {my ($i) = @_; $t->push($i+1)});
    $t->free;
    $a->used->out("Clear tree:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    $a->clear;
    $a->used->out("Clear area:            u: ");
    $a->freeChainSpace->out(" f: ", " ");
    $a->size->outNL;

    ok Assemble eq => <<END, label=>'t5';
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
  t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear tree:            u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
  Clear area:            u: .... .... .... ...0 f: .... .... .... ...0 size of area: .... .... .... 10..
  END


=head2 Iteration

Iterate through a tree non recursively

=head3 Nasm::X86::Tree::by($tree, $block)

Call the specified block with each element of the specified tree in ascending order.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $block     Block to execute

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->put($i, 2 * $i);
     });

    $t->by(sub
     {my ($tree, $start, $next, $end) = @_;
      $tree->key->out("");  $tree->data->outNL(" ");
     });

    ok Assemble eq => <<END, avx512=>1;
  .... .... .... ...0 .... .... .... ...0
  .... .... .... ...1 .... .... .... ...2
  .... .... .... ...2 .... .... .... ...4
  .... .... .... ...3 .... .... .... ...6
  .... .... .... ...4 .... .... .... ...8
  .... .... .... ...5 .... .... .... ...A
  .... .... .... ...6 .... .... .... ...C
  .... .... .... ...7 .... .... .... ...E
  .... .... .... ...8 .... .... .... ..10
  .... .... .... ...9 .... .... .... ..12
  .... .... .... ...A .... .... .... ..14
  .... .... .... ...B .... .... .... ..16
  .... .... .... ...C .... .... .... ..18
  .... .... .... ...D .... .... .... ..1A
  .... .... .... ...E .... .... .... ..1C
  .... .... .... ...F .... .... .... ..1E
  END


=head3 Nasm::X86::Tree::yb($tree, $block)

Call the specified block with each element of the specified tree in descending order.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $block     Block to execute

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->put($i, 2* $i);
     });

    $t->yb(sub
     {my ($tree, $start, $prev, $end) = @_;
      $tree->key->out("");  $tree->data->outNL(" ");
     });

    ok Assemble eq => <<END, avx512=>1;
  .... .... .... ...F .... .... .... ..1E
  .... .... .... ...E .... .... .... ..1C
  .... .... .... ...D .... .... .... ..1A
  .... .... .... ...C .... .... .... ..18
  .... .... .... ...B .... .... .... ..16
  .... .... .... ...A .... .... .... ..14
  .... .... .... ...9 .... .... .... ..12
  .... .... .... ...8 .... .... .... ..10
  .... .... .... ...7 .... .... .... ...E
  .... .... .... ...6 .... .... .... ...C
  .... .... .... ...5 .... .... .... ...A
  .... .... .... ...4 .... .... .... ...8
  .... .... .... ...3 .... .... .... ...6
  .... .... .... ...2 .... .... .... ...4
  .... .... .... ...1 .... .... .... ...2
  .... .... .... ...0 .... .... .... ...0
  END


=head2 Push and Pop

Use a tree as a stack: Push elements on to a tree with the next available key; Pop the last element in a tree.

=head3 Nasm::X86::Tree::peek($tree, $Back)

Peek at the element the specified distance back from the top of the stack and return its B<value> in data and found status in B<found> in the tree descriptor.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $Back      How far back to go with 1 being the top

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->push($i);
     });

    $t->peek(1)->data ->outNL;
    $t->peek(2)->data ->outNL;
    $t->peek(3)->found->outNL;
    $t->peek(2 * $N    )->found->outNL;

    $t->size->outNL;
    $t->get(8); $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;

    $N->for(sub
     {my ($i) = @_;
      $t->pop; $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;
     });
  # $t->pop; $t->found->outNL("f: ");

    ok Assemble eq => <<END, clocks => 29_852;
  data   : .... .... .... ...F
  data   : .... .... .... ...E
  found  : .... .... .... ..40
  found  : .... .... .... ...0
  size of tree: .... .... .... ..10
  f: .... .... .... ...2 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...F data   : .... .... .... ...F
  f: .... .... .... ...1 i: .... .... .... ...E data   : .... .... .... ...E
  f: .... .... .... ...1 i: .... .... .... ...D data   : .... .... .... ...D
  f: .... .... .... ...1 i: .... .... .... ...C data   : .... .... .... ...C
  f: .... .... .... ...1 i: .... .... .... ...B data   : .... .... .... ...B
  f: .... .... .... ...1 i: .... .... .... ...A data   : .... .... .... ...A
  f: .... .... .... ...1 i: .... .... .... ...9 data   : .... .... .... ...9
  f: .... .... .... ...1 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...7 data   : .... .... .... ...7
  f: .... .... .... ...1 i: .... .... .... ...6 data   : .... .... .... ...6
  f: .... .... .... ...1 i: .... .... .... ...5 data   : .... .... .... ...5
  f: .... .... .... ...1 i: .... .... .... ...4 data   : .... .... .... ...4
  f: .... .... .... ...1 i: .... .... .... ...3 data   : .... .... .... ...3
  f: .... .... .... ...1 i: .... .... .... ...2 data   : .... .... .... ...2
  f: .... .... .... ...1 i: .... .... .... ...1 data   : .... .... .... ...1
  f: .... .... .... ...1 i: .... .... .... ...0 data   : .... .... .... ...0
  END


=head3 Nasm::X86::Tree::peekSubTree($tree, $Back)

Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $Back      How far back to go with 1 being the top

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;

    $T->push(0x22);
    $t->push($T);
    my $u = $t->peekSubTree(1);
    $u->peek(1);
    $u->found->outNL;
    $u->data->outNL;
    ok Assemble eq => <<END, avx512=>1;
  found  : .... .... .... ...1
  data   : .... .... .... ..22
  END


=head3 Nasm::X86::Tree::pop($tree)

Pop the last value out of a tree and return the key/data/subTree in the tree descriptor.

     Parameter  Description
  1  $tree      Tree descriptor

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->push($i);
     });

    $t->peek(1)->data ->outNL;
    $t->peek(2)->data ->outNL;
    $t->peek(3)->found->outNL;
    $t->peek(2 * $N    )->found->outNL;

    $t->size->outNL;
    $t->get(8); $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;

    $N->for(sub
     {my ($i) = @_;
      $t->pop; $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;
     });
  # $t->pop; $t->found->outNL("f: ");

    ok Assemble eq => <<END, clocks => 29_852;
  data   : .... .... .... ...F
  data   : .... .... .... ...E
  found  : .... .... .... ..40
  found  : .... .... .... ...0
  size of tree: .... .... .... ..10
  f: .... .... .... ...2 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...F data   : .... .... .... ...F
  f: .... .... .... ...1 i: .... .... .... ...E data   : .... .... .... ...E
  f: .... .... .... ...1 i: .... .... .... ...D data   : .... .... .... ...D
  f: .... .... .... ...1 i: .... .... .... ...C data   : .... .... .... ...C
  f: .... .... .... ...1 i: .... .... .... ...B data   : .... .... .... ...B
  f: .... .... .... ...1 i: .... .... .... ...A data   : .... .... .... ...A
  f: .... .... .... ...1 i: .... .... .... ...9 data   : .... .... .... ...9
  f: .... .... .... ...1 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...7 data   : .... .... .... ...7
  f: .... .... .... ...1 i: .... .... .... ...6 data   : .... .... .... ...6
  f: .... .... .... ...1 i: .... .... .... ...5 data   : .... .... .... ...5
  f: .... .... .... ...1 i: .... .... .... ...4 data   : .... .... .... ...4
  f: .... .... .... ...1 i: .... .... .... ...3 data   : .... .... .... ...3
  f: .... .... .... ...1 i: .... .... .... ...2 data   : .... .... .... ...2
  f: .... .... .... ...1 i: .... .... .... ...1 data   : .... .... .... ...1
  f: .... .... .... ...1 i: .... .... .... ...0 data   : .... .... .... ...0
  END


=head3 Nasm::X86::Tree::popSubTree($tree, %options)

Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.

     Parameter  Description
  1  $tree      Tree descriptor
  2  %options   Options describing the sub tree

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;
       $T->push(K key => 0x333);

    $t->push($T);
    $t->push(K key => 0x22);

    $t->pop;
    $t->found->outNL;
    $t->data->outNL;
    $t->subTree->outNL;

    my $P = $t->popSubTree;
    $t->found->outNL;
    $t->data->outNL;
    $t->subTree->outNL;
    PrintOutStringNL "PPPPP";
    $P->find(K key => 0);
    $P->found->outNL;
    $P->data->outNL;

    ok Assemble eq => <<END, avx512=>1;
  found  : .... .... .... ...1
  data   : .... .... .... ..22
  subTree: .... .... .... ...0
  found  : .... .... .... ...1
  data   : .... .... .... ..80
  subTree: .... .... .... ...1
  PPPPP
  found  : .... .... .... ...1
  data   : .... .... .... .333
  END


=head3 Nasm::X86::Tree::get($tree, $Key)

Retrieves the element at the specified zero based index in the stack.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $Key       Zero based index

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->push($i);
     });

    $t->peek(1)->data ->outNL;
    $t->peek(2)->data ->outNL;
    $t->peek(3)->found->outNL;
    $t->peek(2 * $N    )->found->outNL;

    $t->size->outNL;
    $t->get(8); $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;

    $N->for(sub
     {my ($i) = @_;
      $t->pop; $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;
     });
  # $t->pop; $t->found->outNL("f: ");

    ok Assemble eq => <<END, clocks => 29_852;
  data   : .... .... .... ...F
  data   : .... .... .... ...E
  found  : .... .... .... ..40
  found  : .... .... .... ...0
  size of tree: .... .... .... ..10
  f: .... .... .... ...2 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...F data   : .... .... .... ...F
  f: .... .... .... ...1 i: .... .... .... ...E data   : .... .... .... ...E
  f: .... .... .... ...1 i: .... .... .... ...D data   : .... .... .... ...D
  f: .... .... .... ...1 i: .... .... .... ...C data   : .... .... .... ...C
  f: .... .... .... ...1 i: .... .... .... ...B data   : .... .... .... ...B
  f: .... .... .... ...1 i: .... .... .... ...A data   : .... .... .... ...A
  f: .... .... .... ...1 i: .... .... .... ...9 data   : .... .... .... ...9
  f: .... .... .... ...1 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...7 data   : .... .... .... ...7
  f: .... .... .... ...1 i: .... .... .... ...6 data   : .... .... .... ...6
  f: .... .... .... ...1 i: .... .... .... ...5 data   : .... .... .... ...5
  f: .... .... .... ...1 i: .... .... .... ...4 data   : .... .... .... ...4
  f: .... .... .... ...1 i: .... .... .... ...3 data   : .... .... .... ...3
  f: .... .... .... ...1 i: .... .... .... ...2 data   : .... .... .... ...2
  f: .... .... .... ...1 i: .... .... .... ...1 data   : .... .... .... ...1
  f: .... .... .... ...1 i: .... .... .... ...0 data   : .... .... .... ...0
  END


=head2 Trees as Strings

Use trees as strings of dwords.  The size of the tree is the length of the string. Each dword is consider as an indivisible unit. This arrangement allows the normal string operations of concatenation and substring to be performed easily.

=head3 Nasm::X86::Tree::appendAscii($string, $address, $size)

Append ascii bytes in memory to a tree acting as a string. The address and size of the source memory are specified via variables. Each byte should represent a valid ascii byte so that it can be considered, when left extended with 24 zero bits, as utf32.

     Parameter  Description
  1  $string    Tree descriptor of string to append to
  2  $address   Variable address of memory to append from
  3  $size      Variable size of memory

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = Rb(0x41..0x51);
    $t->appendAscii(K(address=> $b), K(size => 1));
    $t->outAsUtf8NL;
    my $T = $a->CreateTree;
    $T->append($t);
    $T->append($t);
    $T->outAsUtf8NL;
    $T->appendAscii(K(address=> $b) + 1, K(size => 1));
    my $c = $T->clone;
    $c->outAsUtf8NL;
    my $d = $c->substring(1, 3);
    $d->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  A
  AA
  AAB
  AB
  END


=head3 Nasm::X86::Tree::append($string, $append)

Append the second source string to the first target string renumbering the keys of the source string to follow on from those of the target string.  A string can safely be appended to itself.

     Parameter  Description
  1  $string    Tree descriptor of string to append to
  2  $append    Tree descriptor of string to append from

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->push(K alpha => 0x03b1);
    $t->push(K beta  => 0x03b2);
    $t->push(K gamma => 0x03b3);
    $t->push(K delta => 0x03b4);

    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    my $T = $t->substring(K(key => 4), K(key => 8));
    $T->outAsUtf8NL;

    my $r = $T->reverse;
    $r->outAsUtf8NL;

    ok Assemble eq => <<END, avx512=>1;
  αβγδ
  αβγδαβγδ
  αβγδαβγδαβγδαβγδ
  αβγδ
  δγβα
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = Rb(0x41..0x51);
    $t->appendAscii(K(address=> $b), K(size => 1));
    $t->outAsUtf8NL;
    my $T = $a->CreateTree;
    $T->append($t);
    $T->append($t);
    $T->outAsUtf8NL;
    $T->appendAscii(K(address=> $b) + 1, K(size => 1));
    my $c = $T->clone;
    $c->outAsUtf8NL;
    my $d = $c->substring(1, 3);
    $d->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  A
  AA
  AAB
  AB
  END

    my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

    my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
     {my ($p, $s, $sub) = @_;

      Subroutine                                                                  # A contained routine that we wish to export to a file
       {my ($p, $s, $sub) = @_;
        PrintOutString "Ascii: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Ascii",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Add";
       } name => "＋",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Times";
       } name => "✕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "And";
       } name => "𝕒𝕟𝕕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];


      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutString "Variable: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Variable",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

     } name => "operators",  parameters=>[qw(a b c)], export => $f;

    ok Assemble eq => <<END, avx512=>1;
  END

    my $l = ReadArea $f;                                                          # Area containing subroutine library

    my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse

    my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table

   $p->dumpParseResult;

   $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

  # ✕ 2715
  # ＋ ff0b

    ok Assemble eq => <<END, clocks=>20_921;
  parseChar  : .... .... ...1 D5D6
  parseFail  : .... .... .... ...0
  position   : .... .... .... ..38
  parseMatch : .... .... .... ...0
  parseReason: .... .... .... ...0
  𝕒𝕟𝕕
  ._＋
  ._._✕
  ._._._＋
  ._._._._1
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  ._＋
  ._._＋
  ._._._✕
  ._._._._2
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  Ascii: 1
  Variable: 𝗔
  Add
  Variable: 𝗕
  Times
  Variable: 𝗖
  Add
  Ascii: 2
  Variable: 𝗔
  Times
  Variable: 𝗕
  Add
  Variable: 𝗖
  Add
  And
  END
    unlink $f;


=head3 Nasm::X86::Tree::clone($string)

Clone a string.

     Parameter  Description
  1  $string    Tree descriptor

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = Rb(0x41..0x51);
    $t->appendAscii(K(address=> $b), K(size => 1));
    $t->outAsUtf8NL;
    my $T = $a->CreateTree;
    $T->append($t);
    $T->append($t);
    $T->outAsUtf8NL;
    $T->appendAscii(K(address=> $b) + 1, K(size => 1));
    my $c = $T->clone;
    $c->outAsUtf8NL;
    my $d = $c->substring(1, 3);
    $d->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  A
  AA
  AAB
  AB
  END


=head3 Nasm::X86::Tree::substring($string, $Start, $Finish)

Create the substring of the specified string between the specified start and end keys.

     Parameter  Description
  1  $string    Tree descriptor of string to extract from
  2  $Start     Start key
  3  $Finish    End key

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->push(K alpha => 0x03b1);
    $t->push(K beta  => 0x03b2);
    $t->push(K gamma => 0x03b3);
    $t->push(K delta => 0x03b4);

    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    my $T = $t->substring(K(key => 4), K(key => 8));
    $T->outAsUtf8NL;

    my $r = $T->reverse;
    $r->outAsUtf8NL;

    ok Assemble eq => <<END, avx512=>1;
  αβγδ
  αβγδαβγδ
  αβγδαβγδαβγδαβγδ
  αβγδ
  δγβα
  END


=head3 Nasm::X86::Tree::reverse($string)

Create a clone of the string in reverse order.

     Parameter  Description
  1  $string    Tree descriptor of string

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->push(K alpha => 0x03b1);
    $t->push(K beta  => 0x03b2);
    $t->push(K gamma => 0x03b3);
    $t->push(K delta => 0x03b4);

    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    my $T = $t->substring(K(key => 4), K(key => 8));
    $T->outAsUtf8NL;

    my $r = $T->reverse;
    $r->outAsUtf8NL;

    ok Assemble eq => <<END, avx512=>1;
  αβγδ
  αβγδαβγδ
  αβγδαβγδαβγδαβγδ
  αβγδ
  δγβα
  END


=head2 Trees as sets

Trees of trees as sets

=head3 Nasm::X86::Tree::union($tree)

Given a tree of trees consider each sub tree as a set and form the union of all these sets as a new tree.

     Parameter  Description
  1  $tree      Tree descriptor for a tree of trees

B<Example:>


    my $a = CreateArea;
    my $r = $a->CreateTree;
    my $s = $a->CreateTree;
    my $t = $a->CreateTree;

    $r->put(K(key => 1), K(data => 1));
    $r->put(K(key => 2), K(data => 2));

    $s->put(K(key => 1), K(data => 1));
    $s->put(K(key => 3), K(data => 3));

    $t->push($r);
    $t->push($s);

    my $u = $t->union;
    $t->dump('input 1 2  1 3');
    $u->dump('union 1 2    3');

    my $i = $t->intersection;
    $i->dump('intersection 1');

    ok Assemble eq => <<END, avx512=>1;
  input 1 2  1 3
  At:  280                    length:    2,  data:  2C0,  nodes:  300,  first:   C0, root, leaf,  trees:            11
    Index:    0    1
    Keys :    0    1
    Data :  10*  1C*
       At:  100               length:    2,  data:  140,  nodes:  180,  first:   40, root, leaf
         Index:    0    1
         Keys :    1    2
         Data :    1    2
       end
       At:  1C0               length:    2,  data:  200,  nodes:  240,  first:   80, root, leaf
         Index:    0    1
         Keys :    1    3
         Data :    1    3
       end
  end
  union 1 2    3
  At:  380                    length:    3,  data:  3C0,  nodes:  400,  first:  340, root, leaf
    Index:    0    1    2
    Keys :    1    2    3
    Data :    1    2    3
  end
  intersection 1
  At:  480                    length:    1,  data:  4C0,  nodes:  500,  first:  440, root, leaf
    Index:    0
    Keys :    1
    Data :    1
  end
  END


=head3 Nasm::X86::Tree::intersection($tree)

Given a tree of trees consider each sub tree as a set and form the intersection of all these sets as a new tree.

     Parameter  Description
  1  $tree      Tree descriptor for a tree of trees

B<Example:>


    my $a = CreateArea;
    my $r = $a->CreateTree;
    my $s = $a->CreateTree;
    my $t = $a->CreateTree;

    $r->put(K(key => 1), K(data => 1));
    $r->put(K(key => 2), K(data => 2));

    $s->put(K(key => 1), K(data => 1));
    $s->put(K(key => 3), K(data => 3));

    $t->push($r);
    $t->push($s);

    my $u = $t->union;
    $t->dump('input 1 2  1 3');
    $u->dump('union 1 2    3');

    my $i = $t->intersection;
    $i->dump('intersection 1');

    ok Assemble eq => <<END, avx512=>1;
  input 1 2  1 3
  At:  280                    length:    2,  data:  2C0,  nodes:  300,  first:   C0, root, leaf,  trees:            11
    Index:    0    1
    Keys :    0    1
    Data :  10*  1C*
       At:  100               length:    2,  data:  140,  nodes:  180,  first:   40, root, leaf
         Index:    0    1
         Keys :    1    2
         Data :    1    2
       end
       At:  1C0               length:    2,  data:  200,  nodes:  240,  first:   80, root, leaf
         Index:    0    1
         Keys :    1    3
         Data :    1    3
       end
  end
  union 1 2    3
  At:  380                    length:    3,  data:  3C0,  nodes:  400,  first:  340, root, leaf
    Index:    0    1    2
    Keys :    1    2    3
    Data :    1    2    3
  end
  intersection 1
  At:  480                    length:    1,  data:  4C0,  nodes:  500,  first:  440, root, leaf
    Index:    0
    Keys :    1
    Data :    1
  end
  END


=head2 Key String Trees

A key string tree has strings for keys.

=head3 Nasm::X86::Tree::getKeyString($tree, $address, $size)

Find a string in a string tree and return the associated data and find status in the data and found fields of the tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $address   Address of key
  3  $size      Length of key

B<Example:>


    my $t = CreateArea->CreateTree(stringTree=>1);

    K(loop => 3)->for(sub
     {my $a = $t->uniqueKeyString(constantString("aaaa")); $a->outNL;
      my $b = $t->uniqueKeyString(constantString("bbbb")); $b->outNL;
     });

    for my $k(qw(aaaa bbbb ccccc))
     {PrintOutStringNL $k;
      $t->getKeyString(constantString $k); $t->found->outNL; $t->data->outNL;
     }

    ok Assemble eq => <<END, clocks=>4374;
  count: .... .... .... ...0
  count: .... .... .... ...1
  count: .... .... .... ...0
  count: .... .... .... ...1
  count: .... .... .... ...0
  count: .... .... .... ...1
  aaaa
  found  : .... .... .... ...1
  data   : .... .... .... ...0
  bbbb
  found  : .... .... .... ...2
  data   : .... .... .... ...1
  ccccc
  found  : .... .... .... ...0
  data   : .... .... .... ...0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree(stringTree=>1);
    $t->putKeyString(constantString("dddd444"),  K(data => 8));
    $t->putKeyString(constantString("dddd4444"), K(data => 9));
    $t->putKeyString(constantString("eeee5555"), K(data =>10));
    $t->putKeyString(constantString("bbbb2222"), K(data => 2));
    $t->putKeyString(constantString("cccc3333"), K(data => 7));
    $t->putKeyString(constantString("aaaa1111"), K(data => 1));
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443"),  K data => 6);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442"),  K data => 5);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441"),  K data => 4);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440"),  K data => 3);
    $a->dump("AA", K(depth => 20));
    $t->dump("TT");

    $t->getKeyString(constantString("aaaa1111"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("bbbb2222"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443")); $t->data->outNL;
    $t->getKeyString(constantString("cccc3333"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("dddd444"));                                                           $t->data->outNL;
    $t->getKeyString(constantString("dddd4444"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("eeee5555"));                                                          $t->data->outNL;

    ok Assemble eq => <<END, avx512=>1;
  AA
  Area     Size:     4096    Used:     1280
  .... .... .... ...0 | __10 ____ ____ ____  __.5 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | C0__ ____ .1__ ____  .7__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 6464 6464 3434 34__  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | 80.2 ____ __.2 ____  C0.2 ____ 40.2 ____  80__ ____ 80.1 ____  C0.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .7__ .4__ __.1 ____
  .... .... .... .1.. | .1__ ____ .2__ ____  __.3 ____ .7__ ____  .8__ ____ .9__ ____  .A__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
  .... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
  .... .... .... .180 | 6464 6464 3434 3434  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .1C0 | 6565 6565 3535 3535  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2.. | 6262 6262 3232 3232  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .240 | 6363 6363 3333 3333  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .280 | 6161 6161 3131 3131  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2C0 | 6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434  6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434
  .... .... .... .3.. | 80.3 ____ .1__ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .340 | 33__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .380 | C0.4 ____ 80.4 ____  40.4 ____ 40.3 ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0.3 ____
  .... .... .... .3C0 | .3__ ____ .4__ ____  .5__ ____ .6__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.4 ____
  .... .... .... .4.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.3 ____
  .... .... .... .440 | 32__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .480 | 31__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .4C0 | 30__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  TT
  At:   C0                    length:    7,  data:  100,  nodes:  140,  first:   40, root, leaf,  trees:           100
    Index:    0    1    2    3    4    5    6
    Keys :  280  200  2C0  240   80  180  1C0
    Data :    1    2  38*    7    8    9   10
       At:  380               length:    4,  data:  3C0,  nodes:  400,  first:  300, root, leaf
         Index:    0    1    2    3
         Keys :  4C0  480  440  340
         Data :    3    4    5    6
       end
  end
  data   : .... .... .... ...1
  data   : .... .... .... ...2
  data   : .... .... .... ...3
  data   : .... .... .... ...4
  data   : .... .... .... ...5
  data   : .... .... .... ...6
  data   : .... .... .... ...7
  data   : .... .... .... ...8
  data   : .... .... .... ...9
  data   : .... .... .... ...A
  END


=head3 Nasm::X86::Tree::putKeyString($tree, $address, $size, $data)

Associate a string of any length with a double word.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $address   Address of key
  3  $size      Length of key
  4  $data      Data associated with key

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree(stringTree=>1);
    $t->putKeyString(constantString("dddd444"),  K(data => 8));
    $t->putKeyString(constantString("dddd4444"), K(data => 9));
    $t->putKeyString(constantString("eeee5555"), K(data =>10));
    $t->putKeyString(constantString("bbbb2222"), K(data => 2));
    $t->putKeyString(constantString("cccc3333"), K(data => 7));
    $t->putKeyString(constantString("aaaa1111"), K(data => 1));
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443"),  K data => 6);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442"),  K data => 5);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441"),  K data => 4);
    $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440"),  K data => 3);
    $a->dump("AA", K(depth => 20));
    $t->dump("TT");

    $t->getKeyString(constantString("aaaa1111"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("bbbb2222"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442")); $t->data->outNL;
    $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443")); $t->data->outNL;
    $t->getKeyString(constantString("cccc3333"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("dddd444"));                                                           $t->data->outNL;
    $t->getKeyString(constantString("dddd4444"));                                                          $t->data->outNL;
    $t->getKeyString(constantString("eeee5555"));                                                          $t->data->outNL;

    ok Assemble eq => <<END, avx512=>1;
  AA
  Area     Size:     4096    Used:     1280
  .... .... .... ...0 | __10 ____ ____ ____  __.5 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | C0__ ____ .1__ ____  .7__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | 6464 6464 3434 34__  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | 80.2 ____ __.2 ____  C0.2 ____ 40.2 ____  80__ ____ 80.1 ____  C0.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .7__ .4__ __.1 ____
  .... .... .... .1.. | .1__ ____ .2__ ____  __.3 ____ .7__ ____  .8__ ____ .9__ ____  .A__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
  .... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
  .... .... .... .180 | 6464 6464 3434 3434  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .1C0 | 6565 6565 3535 3535  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2.. | 6262 6262 3232 3232  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .240 | 6363 6363 3333 3333  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .280 | 6161 6161 3131 3131  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .2C0 | 6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434  6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434
  .... .... .... .3.. | 80.3 ____ .1__ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .340 | 33__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .380 | C0.4 ____ 80.4 ____  40.4 ____ 40.3 ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0.3 ____
  .... .... .... .3C0 | .3__ ____ .4__ ____  .5__ ____ .6__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.4 ____
  .... .... .... .4.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.3 ____
  .... .... .... .440 | 32__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .480 | 31__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... .4C0 | 30__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  TT
  At:   C0                    length:    7,  data:  100,  nodes:  140,  first:   40, root, leaf,  trees:           100
    Index:    0    1    2    3    4    5    6
    Keys :  280  200  2C0  240   80  180  1C0
    Data :    1    2  38*    7    8    9   10
       At:  380               length:    4,  data:  3C0,  nodes:  400,  first:  300, root, leaf
         Index:    0    1    2    3
         Keys :  4C0  480  440  340
         Data :    3    4    5    6
       end
  end
  data   : .... .... .... ...1
  data   : .... .... .... ...2
  data   : .... .... .... ...3
  data   : .... .... .... ...4
  data   : .... .... .... ...5
  data   : .... .... .... ...6
  data   : .... .... .... ...7
  data   : .... .... .... ...8
  data   : .... .... .... ...9
  data   : .... .... .... ...A
  END


=head3 Nasm::X86::Tree::uniqueKeyString($tree, $address, $size)

Add a key string to a string tree if the key is not already present and return a unique number identifying the string (although currently there is no way to fast way to recover the string from the number). If the key string is already present in the string tree return the number associated with the original key string rather than creating a new entry.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $address   Address of key
  3  $size      Length of key

B<Example:>


    my $t = CreateArea->CreateTree(stringTree=>1);

    K(loop => 3)->for(sub
     {my $a = $t->uniqueKeyString(constantString("aaaa")); $a->outNL;
      my $b = $t->uniqueKeyString(constantString("bbbb")); $b->outNL;
     });

    for my $k(qw(aaaa bbbb ccccc))
     {PrintOutStringNL $k;
      $t->getKeyString(constantString $k); $t->found->outNL; $t->data->outNL;
     }

    ok Assemble eq => <<END, clocks=>4374;
  count: .... .... .... ...0
  count: .... .... .... ...1
  count: .... .... .... ...0
  count: .... .... .... ...1
  count: .... .... .... ...0
  count: .... .... .... ...1
  aaaa
  found  : .... .... .... ...1
  data   : .... .... .... ...0
  bbbb
  found  : .... .... .... ...2
  data   : .... .... .... ...1
  ccccc
  found  : .... .... .... ...0
  data   : .... .... .... ...0
  END


=head2 Print

Print a tree

=head3 Nasm::X86::Tree::printInOrder($tree, $title)

Print a tree in order.

     Parameter  Description
  1  $tree      Tree
  2  $title     Title

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K count => 128;

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $l0 = ($N-$index) / 2;
      my $l1 = ($N+$index) / 2;
      my $h0 =  $N-$index;
      my $h1 =  $N+$index;
      $t->put($l0, $l0 * 2);
      $t->put($h1, $h1 * 2);
      $t->put($l1, $l1 * 2);
      $t->put($h0, $h0 * 2);
     });

    $t->printInOrder("AAAA");

    ok Assemble eq => <<END, label=>'t3';
  AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K count => 128;

    $N->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $l0 = ($N-$index) / 2;
      my $l1 = ($N+$index) / 2;
      my $h0 =  $N-$index;
      my $h1 =  $N+$index;
      $t->put($l0, $l0 * 2);
      $t->put($h1, $h1 * 2);
      $t->put($l1, $l1 * 2);
      $t->put($h0, $h0 * 2);
     });
    $t->printInOrder("AAAA");

    ok Assemble eq => <<END, avx512=>1;
  AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
  END


=head3 Nasm::X86::Tree::outAsUtf8($string)

Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8.

     Parameter  Description
  1  $string    Tree descriptor of string

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    $t->push(K alpha => 0x03b1);
    $t->push(K beta  => 0x03b2);
    $t->push(K gamma => 0x03b3);
    $t->push(K delta => 0x03b4);

    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    $t->append($t);
    $t->outAsUtf8NL;

    my $T = $t->substring(K(key => 4), K(key => 8));
    $T->outAsUtf8NL;

    my $r = $T->reverse;
    $r->outAsUtf8NL;

    ok Assemble eq => <<END, avx512=>1;
  αβγδ
  αβγδαβγδ
  αβγδαβγδαβγδαβγδ
  αβγδ
  δγβα
  END

    my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

    my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
     {my ($p, $s, $sub) = @_;

      Subroutine                                                                  # A contained routine that we wish to export to a file
       {my ($p, $s, $sub) = @_;
        PrintOutString "Ascii: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Ascii",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Add";
       } name => "＋",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Times";
       } name => "✕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "And";
       } name => "𝕒𝕟𝕕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];


      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutString "Variable: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Variable",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

     } name => "operators",  parameters=>[qw(a b c)], export => $f;

    ok Assemble eq => <<END, avx512=>1;
  END

    my $l = ReadArea $f;                                                          # Area containing subroutine library

    my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse

    my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table

   $p->dumpParseResult;

   $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

  # ✕ 2715
  # ＋ ff0b

    ok Assemble eq => <<END, clocks=>20_921;
  parseChar  : .... .... ...1 D5D6
  parseFail  : .... .... .... ...0
  position   : .... .... .... ..38
  parseMatch : .... .... .... ...0
  parseReason: .... .... .... ...0
  𝕒𝕟𝕕
  ._＋
  ._._✕
  ._._._＋
  ._._._._1
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  ._＋
  ._._＋
  ._._._✕
  ._._._._2
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  Ascii: 1
  Variable: 𝗔
  Add
  Variable: 𝗕
  Times
  Variable: 𝗖
  Add
  Ascii: 2
  Variable: 𝗔
  Times
  Variable: 𝗕
  Add
  Variable: 𝗖
  Add
  And
  END
    unlink $f;


=head3 Nasm::X86::Tree::outAsUtf8NL($string)

Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8. Follow the print with a new line character.

     Parameter  Description
  1  $string    Tree descriptor of string

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = Rb(0x41..0x51);
    $t->appendAscii(K(address=> $b), K(size => 1));
    $t->outAsUtf8NL;
    my $T = $a->CreateTree;
    $T->append($t);
    $T->append($t);
    $T->outAsUtf8NL;
    $T->appendAscii(K(address=> $b) + 1, K(size => 1));
    my $c = $T->clone;
    $c->outAsUtf8NL;
    my $d = $c->substring(1, 3);
    $d->outAsUtf8NL;
    ok Assemble eq => <<END, avx512=>1;
  A
  AA
  AAB
  AB
  END


=head1 Unisyn

Parse Unisyn language statements.

=head2 Lex

Lexical Analysis

=head3 Nasm::X86::Unisyn::Lex::composeUnisyn($words)

Compose phrases of Unisyn and return them as a string

     Parameter  Description
  1  $words     String of words

B<Example:>


  if (1)

   {my $t = Nasm::X86::Unisyn::Lex::composeUnisyn "va w m+ w vb";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    is_deeply $t, "𝗮 ＋ 𝗯";
    my $p = &ParseUnisyn(constantString $t);
    $p->dumpPostOrder;
    ok Assemble eq => <<END;
  ._𝗮 
  ._𝗯
  ＋ 
  END
   }


=head3 Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray()

Create and load the table of lexical transitions.


B<Example:>


    my $a = Nasm::X86::Unisyn::Lex::Number::a;                                    # Assign-2 - right to left
    my $b = Nasm::X86::Unisyn::Lex::Number::b;                                    # Open


    my ($N, $A) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, $A;
    Add rax, ($a << Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits) + $a;
    Mov al, "[rax]";
    And rax, 0xff;
    PrintOutRegisterInHex rax;

    Mov rax, $A;
    Add rax, ($b << Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits) + $b;
    Mov al, "[rax]";
    And rax, 0xff;
    PrintOutRegisterInHex rax;
    ok Assemble eq => <<END, avx512=>1;
     rax: .... .... .... ..FF
     rax: .... .... .... ...1
  END

  if (1)                                                                          # Place parser tables into an area
   {my $a = CreateArea;
    my ($alphabetN,    $alphabetA)    = Nasm::X86::Unisyn::Lex::AlphabetsArray;

    my ($transitionsN, $transitionsA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    $a->appendVar(V address => "[$transitionsN]");
    $a->appendVar(V address => "[$alphabetN]");                                   # Save sizes at start if area where they can be easily found

    $a->appendMemory(V(address => $transitionsA), V size => "[$transitionsN]");   # Save transitions
    $a->appendMemory(V(address => $alphabetA),    V size => "[$alphabetN]");      # Save alphabets classification

    $a->write("z123.txt");                                                        # Save the area to the named file
    ok Assemble eq => <<END;
  END
   }


=head3 Nasm::X86::Unisyn::Lex::AlphabetsArray()

Create an array of utf32 to alphabet number.


B<Example:>



    my ($N, $A) = Nasm::X86::Unisyn::Lex::AlphabetsArray;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rax, "[$N]";
    PrintOutRegisterInHex rax;
    Mov rax, $A;
    Add rax, ord('𝝰');
    Mov al, "[rax]";
    And rax, 0xff;
    PrintOutRegisterInHex rax;

    Mov rax, $A;
    Add rax, ord('𝔸');
    Mov al, "[rax]";
    And rax, 0xff;
    PrintOutRegisterInHex rax;

    my sub expect($)
     {my ($n) = @_;                                                               # Parameters
      $n >= 0x10 ? sprintf("%2X", $n) : sprintf(".%1X", $n)
     }

    my $a = expect Nasm::X86::Unisyn::Lex::Number::v;
    my $b = expect Nasm::X86::Unisyn::Lex::Number::d;
    ok Assemble eq => <<END, avx512=>1;
     rax: .... .... ...1 EEF2
     rax: .... .... .... ..$a
     rax: .... .... .... ..$b
  END

  if (1)                                                                          # Place parser tables into an area
   {my $a = CreateArea;

    my ($alphabetN,    $alphabetA)    = Nasm::X86::Unisyn::Lex::AlphabetsArray;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my ($transitionsN, $transitionsA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;

    $a->appendVar(V address => "[$transitionsN]");
    $a->appendVar(V address => "[$alphabetN]");                                   # Save sizes at start if area where they can be easily found

    $a->appendMemory(V(address => $transitionsA), V size => "[$transitionsN]");   # Save transitions
    $a->appendMemory(V(address => $alphabetA),    V size => "[$alphabetN]");      # Save alphabets classification

    $a->write("z123.txt");                                                        # Save the area to the named file
    ok Assemble eq => <<END;
  END
   }


=head3 Nasm::X86::Unisyn::Lex::letterToNumber()

Map each letter in the union of the alphabets to a sequential number


B<Example:>


  if (1)

   {my ($n, $l) = Nasm::X86::Unisyn::Lex::letterToNumber;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    my ($N, $L) = Nasm::X86::Unisyn::Lex::numberToLetter;

    If $N > $n,
    Then
     {PrintErrTraceBack "Numbers to letters cannot be greater than letters to numbers";
     };

    $l->setReg(rax);
    $L->setReg(rbx);

    $n->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->setReg(rsi);                                                            # The character we want to test as utf32

      Mov edx, "[rax+4*rsi]";                                                     # The unisyn number of a letter
      Cmp edx, -1;
      Je $next;                                                                   # Not a unisyn chaarcter

      Mov r15d, "[rbx+4*rdx]";                                                    # r15 contains the utf32 representation of a unisyn letter

      Cmp r15, rsi;                                                               # Check that we retirved th chaarcter westarted with
      IfNe
      Then
       {$i->outNL("Mismatch: ");
        PrintOutRegisterInHex rsi, r15;
       }
     });

    ok Assemble eq => <<END;
  END
   }


=head3 Nasm::X86::Unisyn::Lex::numberToLetter()

Recover a letter from its unique number


B<Example:>


  if (1)
   {my ($n, $l) = Nasm::X86::Unisyn::Lex::letterToNumber;

    my ($N, $L) = Nasm::X86::Unisyn::Lex::numberToLetter;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    If $N > $n,
    Then
     {PrintErrTraceBack "Numbers to letters cannot be greater than letters to numbers";
     };

    $l->setReg(rax);
    $L->setReg(rbx);

    $n->for(sub
     {my ($i, $start, $next, $end) = @_;
      $i->setReg(rsi);                                                            # The character we want to test as utf32

      Mov edx, "[rax+4*rsi]";                                                     # The unisyn number of a letter
      Cmp edx, -1;
      Je $next;                                                                   # Not a unisyn chaarcter

      Mov r15d, "[rbx+4*rdx]";                                                    # r15 contains the utf32 representation of a unisyn letter

      Cmp r15, rsi;                                                               # Check that we retirved th chaarcter westarted with
      IfNe
      Then
       {$i->outNL("Mismatch: ");
        PrintOutRegisterInHex rsi, r15;
       }
     });

    ok Assemble eq => <<END;
  END
   }


=head2 Parse

Parse Unisyn language statements.

=head3 ParseUnisyn($a8, $s8)

Parse a string of utf8 characters.

     Parameter  Description
  1  $a8        Address of utf8 source string
  2  $s8        Size of utf8 source string in bytes

B<Example:>


    my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

    my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
     {my ($p, $s, $sub) = @_;

      Subroutine                                                                  # A contained routine that we wish to export to a file
       {my ($p, $s, $sub) = @_;
        PrintOutString "Ascii: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Ascii",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Add";
       } name => "＋",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Times";
       } name => "✕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "And";
       } name => "𝕒𝕟𝕕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];


      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutString "Variable: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Variable",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

     } name => "operators",  parameters=>[qw(a b c)], export => $f;

    ok Assemble eq => <<END, avx512=>1;
  END

    my $l = ReadArea $f;                                                          # Area containing subroutine library

    my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse


    my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


   $p->dumpParseResult;

   $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

  # ✕ 2715
  # ＋ ff0b

    ok Assemble eq => <<END, clocks=>20_921;
  parseChar  : .... .... ...1 D5D6
  parseFail  : .... .... .... ...0
  position   : .... .... .... ..38
  parseMatch : .... .... .... ...0
  parseReason: .... .... .... ...0
  𝕒𝕟𝕕
  ._＋
  ._._✕
  ._._._＋
  ._._._._1
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  ._＋
  ._._＋
  ._._._✕
  ._._._._2
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  Ascii: 1
  Variable: 𝗔
  Add
  Variable: 𝗕
  Times
  Variable: 𝗖
  Add
  Ascii: 2
  Variable: 𝗔
  Times
  Variable: 𝗕
  Add
  Variable: 𝗖
  Add
  And
  END
    unlink $f;


=head3 Nasm::X86::Unisyn::Parse::dumpParseResult($parse)

Dump the result of a parse

     Parameter  Description
  1  $parse     Parse

B<Example:>


    my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

    my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
     {my ($p, $s, $sub) = @_;

      Subroutine                                                                  # A contained routine that we wish to export to a file
       {my ($p, $s, $sub) = @_;
        PrintOutString "Ascii: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Ascii",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Add";
       } name => "＋",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Times";
       } name => "✕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "And";
       } name => "𝕒𝕟𝕕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];


      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutString "Variable: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Variable",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

     } name => "operators",  parameters=>[qw(a b c)], export => $f;

    ok Assemble eq => <<END, avx512=>1;
  END

    my $l = ReadArea $f;                                                          # Area containing subroutine library

    my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse

    my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table

   $p->dumpParseResult;

   $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

  # ✕ 2715
  # ＋ ff0b

    ok Assemble eq => <<END, clocks=>20_921;
  parseChar  : .... .... ...1 D5D6
  parseFail  : .... .... .... ...0
  position   : .... .... .... ..38
  parseMatch : .... .... .... ...0
  parseReason: .... .... .... ...0
  𝕒𝕟𝕕
  ._＋
  ._._✕
  ._._._＋
  ._._._._1
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  ._＋
  ._._＋
  ._._._✕
  ._._._._2
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  Ascii: 1
  Variable: 𝗔
  Add
  Variable: 𝗕
  Times
  Variable: 𝗖
  Add
  Ascii: 2
  Variable: 𝗔
  Times
  Variable: 𝗕
  Add
  Variable: 𝗖
  Add
  And
  END
    unlink $f;


=head3 Nasm::X86::Unisyn::Parse::dumpPostOrder($parse)

Dump a parse tree in post order.

     Parameter  Description
  1  $parse     Parse

B<Example:>


    my $u = Nasm::X86::Unisyn::Lex::composeUnisyn("va w m+ vb");
    my $t = '𝗮 ＋𝗯';
    is_deeply $u, $t;

    my $p = &ParseUnisyn(constantString $t);                                      # Parse the utf8 string minus the final new line

    $p->dumpPostOrder;
    ok Assemble eq <<END;
  ._𝗮 
  ._𝗯
  ＋
  END

  if (1)
   {my $t = Nasm::X86::Unisyn::Lex::composeUnisyn "va w m+ w vb";
    is_deeply $t, "𝗮 ＋ 𝗯";
    my $p = &ParseUnisyn(constantString $t);
    $p->dumpPostOrder;
    ok Assemble eq => <<END;
  ._𝗮 
  ._𝗯
  ＋ 
  END
   }


=head3 Nasm::X86::Unisyn::Parse::traverseApplyingLibraryOperators($parse, $library)

Traverse a parse tree, in post order, applying a library of operators.

     Parameter  Description
  1  $parse     Parse tree
  2  $library   Area containing a library

B<Example:>


    my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

    my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
     {my ($p, $s, $sub) = @_;

      Subroutine                                                                  # A contained routine that we wish to export to a file
       {my ($p, $s, $sub) = @_;
        PrintOutString "Ascii: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Ascii",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Add";
       } name => "＋",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "Times";
       } name => "✕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutStringNL "And";
       } name => "𝕒𝕟𝕕",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];


      Subroutine
       {my ($p, $s, $sub) = @_;
        PrintOutString "Variable: ";

        my $parse  = $$s{parse};                                                  # Parse
        my $source = $parse->source;                                              # Source

        $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

        my $w = dSize;
        my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
        my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

       ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

       } name => "Variable",
         structures => {parse => Nasm::X86::Unisyn::DescribeParse},
         parameters => [qw(offset)];

     } name => "operators",  parameters=>[qw(a b c)], export => $f;

    ok Assemble eq => <<END, avx512=>1;
  END

    my $l = ReadArea $f;                                                          # Area containing subroutine library

    my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse

    my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table

   $p->dumpParseResult;

   $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

  # ✕ 2715
  # ＋ ff0b

    ok Assemble eq => <<END, clocks=>20_921;
  parseChar  : .... .... ...1 D5D6
  parseFail  : .... .... .... ...0
  position   : .... .... .... ..38
  parseMatch : .... .... .... ...0
  parseReason: .... .... .... ...0
  𝕒𝕟𝕕
  ._＋
  ._._✕
  ._._._＋
  ._._._._1
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  ._＋
  ._._＋
  ._._._✕
  ._._._._2
  ._._._._𝗔
  ._._._𝗕
  ._._𝗖
  Ascii: 1
  Variable: 𝗔
  Add
  Variable: 𝗕
  Times
  Variable: 𝗖
  Add
  Ascii: 2
  Variable: 𝗔
  Times
  Variable: 𝗕
  Add
  Variable: 𝗖
  Add
  And
  END
    unlink $f;


=head1 Assemble

Assemble generated code

=head2 CallC($sub, @parameters)

Call a C subroutine.

     Parameter    Description
  1  $sub         Name of the sub to call
  2  @parameters  Parameters

B<Example:>


    my $format = Rs "Hello %s
";
    my $data   = Rs "World";

    Extern qw(printf exit malloc strcpy); Link 'c';


    CallC 'malloc', length($format)+1;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r15, rax;

    CallC 'strcpy', r15, $format;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    CallC 'printf', r15, $data;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    CallC 'exit', 0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble avx512=>0, eq => <<END;
  Hello World
  END


=head2 Extern(@externalReferences)

Name external references.

     Parameter            Description
  1  @externalReferences  External references

B<Example:>


    my $format = Rs "Hello %s
";
    my $data   = Rs "World";


    Extern qw(printf exit malloc strcpy); Link 'c';  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    CallC 'malloc', length($format)+1;
    Mov r15, rax;
    CallC 'strcpy', r15, $format;
    CallC 'printf', r15, $data;
    CallC 'exit', 0;

    ok Assemble avx512=>0, eq => <<END;
  Hello World
  END


=head2 Link(@libraries)

Libraries to link with.

     Parameter   Description
  1  @libraries  Link library names which will be looked for on "LIBPATH"

B<Example:>


    my $format = Rs "Hello %s
";
    my $data   = Rs "World";


    Extern qw(printf exit malloc strcpy); Link 'c';  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    CallC 'malloc', length($format)+1;
    Mov r15, rax;
    CallC 'strcpy', r15, $format;
    CallC 'printf', r15, $data;
    CallC 'exit', 0;

    ok Assemble avx512=>0, eq => <<END;
  Hello World
  END


=head2 Start()

Initialize the assembler.


B<Example:>



    Start;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Exit(0);
    ok Assemble eq => <<END;
  END


=head2 Exit($c)

Exit with the specified return code or zero if no return code supplied.  Assemble() automatically adds a call to Exit(0) if the last operation in the program is not a call to Exit.

     Parameter  Description
  1  $c         Return code

B<Example:>


    Start;

    Exit(0);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    ok Assemble eq => <<END;
  END

    Comment "Print a string from memory";
    my $s = "Hello World";
    Mov rax, Rs($s);
    Mov rdi, length $s;
    PrintOutMemory;

    Exit(0);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble(avx512=>0) =~ m(Hello World);


=head2 Assemble(%options)

Assemble the generated code.

     Parameter  Description
  1  %options   Options

B<Example:>


    PrintOutStringNL "Hello World";
    PrintOutStringNL "Hello
World";
    PrintErrStringNL "Hello World";


    ok Assemble eq => <<END, avx512=>0, label=>'t1';  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  Hello World
  Hello
  World
  END


    ok Assemble eq => <<END, mix=>1;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  END


=head1 Awaiting Classification

Routines that have not yet been classified.


=head1 Hash Definitions




=head2 Nasm::X86 Definition


Tree




=head3 Output fields


=head4 B

Log2 of size of initial allocation

=head4 N

Initial allocation

=head4 address

Variable that addresses the memory containing the area

=head4 area

Area definition.

=head4 block

Block used to generate this subroutine

=head4 constant

Constant if true

=head4 data

Variable containing the current data

=head4 dataOffset

The start of the data

=head4 end

End label for this subroutine

=head4 export

File this subroutine was exported to if any

=head4 expr

Expression that initializes the variable

=head4 fcArray

Offset of cached array in first block

=head4 fcControl

Offset of the tree bits and present bits in the first cache of low key values for this tree.

=head4 fcDWidth

Number of dwords available in the first cache.  The first cache supplies an alternate area to store the values of keys less than this value  to fill the otherwise unused space in a way that improves the performance of trees when used to represent small arrays, stacks or structures.

=head4 fcPresent

Offset of the present bits in the control dword

=head4 fcPresentOff

Byte offset of word  containing present bits

=head4 fcTreeBits

Offset of the tree bits in bits in the control dword

=head4 fcTreeBitsOff

Byte offset of word containing tree bits

=head4 first

Variable addressing offset to first block of the tree which is the header block

=head4 found

Variable indicating whether the last find was successful or not

=head4 freeOffset

Free chain offset

=head4 key

Variable containing the current key

=head4 keyDataMask

Key data mask

=head4 label

Address in memory

=head4 length

Number of keys in a maximal block

=head4 lengthLeft

Left minimal number of keys

=head4 lengthMiddle

Number of splitting key counting from 1

=head4 lengthMin

The smallest number of keys we are allowed in any node other than a root node.

=head4 lengthOffset

Offset of length in keys block.  The length field is a word - see: "MultiWayTree.svg"

=head4 lengthRight

Right minimal number of keys

=head4 loop

Offset of keys, data, node loop.

=head4 maxKeys

Maximum number of keys allowed in this tree which might well ne less than the maximum we can store in a zmm.

=head4 maxKeysZ

The maximum possible number of keys in a zmm register

=head4 maxNodesZ

The maximum possible number of nodes in a zmm register

=head4 middleOffset

Offset of the middle slot in bytes

=head4 name

Optional name

=head4 nextOffset

Position of next offset on free chain

=head4 nodeMask

Node mask

=head4 offset

Variable containing the offset of the block containing the current key

=head4 options

Options used by the author of the subroutine

=head4 optionsOffset

Offset of the options double word in the first block

=head4 parameters

Parameters definitions supplied by the author of the subroutine which get mapped in to parameter variables.

=head4 position

Position in stack frame

=head4 reference

Reference to another variable

=head4 rightOffset

Offset of the first right slot in bytes

=head4 rootOffset

Offset of the root field in the first block - the root field contains the offset of the block containing the keys of the root of the tree

=head4 sizeOffset

Offset of the size field  in the first block - tells us the number of  keys in the tree

=head4 splittingKey

Offset at which to split a full block

=head4 stack

MArk the area as being used as a stack

=head4 start

Start label for this subroutine which includes the enter instruction used to create a new stack frame

=head4 stringTree

String tree - now obsolete

=head4 stringTreeBit

Bit indicating string key tree

=head4 stringTreeCountOff

This field is used to count the total number of keys in a string tree so that we can assign unique numbers when pushing.

=head4 structureCopies

Copies of the structures passed to this subroutine with their variables replaced with references

=head4 structureVariables

Map structure variables to references at known positions in the sub

=head4 subTree

Variable indicating whether the last find found a sub tree

=head4 treeBits

Offset of tree bits in keys block.  The tree bits field is a word, each bit of which tells us whether the corresponding data element is the offset (or not) to a sub tree of this tree .

=head4 treeBitsMask

Total of 14 tree bits

=head4 treeOffset

Yggdrasil - a tree of global variables in this area

=head4 up

Offset of up in data block.

=head4 usedOffset

Used field offset

=head4 variables

Map parameters to references at known positions in the sub

=head4 vars

Number of variables in subroutine

=head4 width

Width of a key or data slot.

=head4 zWidth

Width of a zmm register

=head4 zWidthD

Width of a zmm in double words being the element size

=head4 zmmBlock

Size of a zmm block - 64 bytes



=head1 Attributes


The following is a list of all the attributes in this package.  A method coded
with the same name in your package will over ride the method of the same name
in this package and thus provide your value for the attribute in place of the
default value supplied for this attribute by this package.

=head2 Replaceable Attribute List


Pi32 Pi64 bSize dSize qSize wSize


=head2 Pi32

Pi as a 32 bit float.


=head2 Pi64

Pi as a 64 bit float.


=head2 bSize

Size of a byte in bytes


=head2 dSize

Size of a double word in bytes


=head2 qSize

Size of a quad word in bytes


=head2 wSize

Size of a word in bytes




=head1 Private Methods

=head2 Dbwdq($s, @d)

Layout data.

     Parameter  Description
  1  $s         Element size
  2  @d         Data to be laid out

=head2 Rbwdq($s, @d)

Layout data.

     Parameter  Description
  1  $s         Element size
  2  @d         Data to be laid out

=head2 PushRR(@r)

Push registers onto the stack without tracking.

     Parameter  Description
  1  @r         Register

=head2 PopRR(@r)

Pop registers from the stack without tracking.

     Parameter  Description
  1  @r         Register

=head2 registerNameFromNumber($r)

Register name from number where possible.

     Parameter  Description
  1  $r         Register number

=head2 ChooseRegisters($number, @registers)

Choose the specified numbers of registers excluding those on the specified list.

     Parameter   Description
  1  $number     Number of registers needed
  2  @registers  Registers not to choose

=head2 checkZmmRegister($z)

Check that a register is a zmm register.

     Parameter  Description
  1  $z         Parameters

=head2 LoadRegFromMm($mm, $offset, $reg)

Load the specified register from the numbered zmm at the quad offset specified as a constant number.

     Parameter  Description
  1  $mm        Mm register
  2  $offset    Offset in quads
  3  $reg       General purpose register to load

B<Example:>


    Mov rax, 1; SaveRegIntoMm(zmm0, 0, rax);
    Mov rax, 2; SaveRegIntoMm(zmm0, 1, rax);
    Mov rax, 3; SaveRegIntoMm(zmm0, 2, rax);
    Mov rax, 4; SaveRegIntoMm(zmm0, 3, rax);


    LoadRegFromMm(zmm0, 0, r15);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    LoadRegFromMm(zmm0, 1, r14);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    LoadRegFromMm(zmm0, 2, r13);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    LoadRegFromMm(zmm0, 3, r12);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    PrintOutRegisterInHex ymm0, r15, r14, r13, r12;
    ok Assemble(debug => 0, eq => <<END, avx512=>1);
    ymm0: .... .... .... ...4  .... .... .... ...3 - .... .... .... ...2  .... .... .... ...1
     r15: .... .... .... ...1
     r14: .... .... .... ...2
     r13: .... .... .... ...3
     r12: .... .... .... ...4
  END


=head2 extractRegisterNumberFromMM($mm)

Extract the register number from an *mm register.

     Parameter  Description
  1  $mm        Mmm register

=head2 getBwdqFromMm($xyz, $size, $mm, $offset, %options)

Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable.

     Parameter  Description
  1  $xyz       Size of mm
  2  $size      Size of get
  3  $mm        Mm register
  4  $offset    Offset in bytes either as a constant or as a variable
  5  %options   Options

=head2 LoadConstantIntoMaskRegister($mask, $value)

Set a mask register equal to a constant.

     Parameter  Description
  1  $mask      Number of mask register to load
  2  $value     Constant to load

=head2 createBitNumberFromAlternatingPattern($prefix, @values)

Create a number from a bit pattern.

     Parameter  Description
  1  $prefix    Prefix bits
  2  @values    +n 1 bits -n 0 bits

=head2 opposingJump($j)

Return the opposite of a jump.

     Parameter  Description
  1  $j         Jump

B<Example:>



    is_deeply opposingJump(q(Jl)), q(Jge);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    is_deeply opposingJump(q(Jg)), q(Jle);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



=head2 IfS($then, $else)

If signed greater than or equal execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

=head2 IfNs($then, $else)

If signed less than execute the then block else the else block.

     Parameter  Description
  1  $then      Then - required
  2  $else      Else - optional

=head2 SubroutineStartStack()

Initialize a new stack frame.  The first quad of each frame has the address of the name of the sub in the low dword, and the parameter count in the upper byte of the quad.  This field is all zeroes in the initial frame.


=head2 copyStructureMinusVariables($s)

Copy a non recursive structure ignoring variables.

     Parameter  Description
  1  $s         Structure to copy

=head2 Nasm::X86::Subroutine::writeLibraryToArea($s, $subs)

Write a subroutine library to an area then save the area in a file so that the subroutine can be reloaded at a later date either as separate file or via incorporation into a thing.  A thing was originally an assembly of people as in "The Allthing" or the "Stort Thing".

     Parameter  Description
  1  $s         Sub definition of containing subroutine
  2  $subs      Definitions of contained subroutines

=head2 Nasm::X86::Area::readLibraryHeader($area, $uniqueStrings, $singleLetterArray)

Create a tree mapping the numbers assigned to subroutine names to the offsets of the corresponding routines in a library returning the intersection so formed mapping the lexical item numbers (not names) encountered during parsing with the matching routines in the library. Optionally a subroutine (like Nasm::X86::Unisyn::Lex::letterToNumber) can be provided that returns details of an array that maps a single utf32 character to a smaller number which will be assumed to be the number of the routine with that single letter as its name.

     Parameter           Description
  1  $area               Area containing subroutine library
  2  $uniqueStrings      Unique strings from parse
  3  $singleLetterArray  Subroutine returning details of a single character mapping array

B<Example:>


    my $a = CreateArea;

    my $u = $a->CreateTree(stringTree=>1);
    $u->putKeyString(constantString("ab"), K key => 1);
    $u->putKeyString(constantString("ac"), K key => 3);
    $u->putKeyString(constantString("𝕒𝕒"), K key => 4);

    my %s = (ab => 2, 𝕒𝕒 =>8, 𝗮𝗮𝗮=>12, 𝝰𝝰𝝰𝝰=>16);                                 # Subroutine names and offsets

    $a->writeLibraryHeader({%s});
    my ($inter, $subroutines) = $a->readLibraryHeader($u);

    $inter->dump("TT");

    ok Assemble eq => <<END, clocks=>4374;
  TT
  At:  1C0                    length:    2,  data:  200,  nodes:  240,  first:   40, root, leaf
    Index:    0    1
    Keys :    1    4
    Data :    2    8
  end
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    K(loop => 16)->for(sub
     {my ($i, $start, $next, $end) = @_;
      my $T = $a->CreateTree;
      $t->put($i, $T);
      $t->size->outNL;
     });

    K(loop => 16)->for(sub
     {my ($i, $start, $next, $end) = @_;
      $t->popSubTree;
      $t->size->outNL;
     });

    ok Assemble eq => <<END, avx512=>1;
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ..10
  size of tree: .... .... .... ...F
  size of tree: .... .... .... ...E
  size of tree: .... .... .... ...D
  size of tree: .... .... .... ...C
  size of tree: .... .... .... ...B
  size of tree: .... .... .... ...A
  size of tree: .... .... .... ...9
  size of tree: .... .... .... ...8
  size of tree: .... .... .... ...7
  size of tree: .... .... .... ...6
  size of tree: .... .... .... ...5
  size of tree: .... .... .... ...4
  size of tree: .... .... .... ...3
  size of tree: .... .... .... ...2
  size of tree: .... .... .... ...1
  size of tree: .... .... .... ...0
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;
    $t->push($T);
    $a->dump("AA");
    $t->popSubTree;
    $a->dump("BB");

    ok Assemble eq => <<END, avx512=>1;
  AA
  Area     Size:     4096    Used:      384
  .... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | C0__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
  BB
  Area     Size:     4096    Used:      384
  .... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
  END


=head2 Nasm::X86::Subroutine::subroutineDefinition($area, $file, $name)

Get the definition of a subroutine from an area.

     Parameter  Description
  1  $area      Area - but only to get easy access to this routine
  2  $file      File containing area
  3  $name      Name of subroutine whose details we want

=head2 Nasm::X86::Subroutine::mapStructureVariables($sub, $S, @P)

Find the paths to variables in the copies of the structures passed as parameters and replace those variables with references so that in the subroutine we can refer to these variables regardless of where they are actually defined.

     Parameter  Description
  1  $sub       Sub definition
  2  $S         Copies of source structures
  3  @P         Path through copies of source structures to a variable that becomes a reference

=head2 Nasm::X86::Subroutine::uploadStructureVariablesToNewStackFrame($sub, $sv, $S, @P)

Create references to variables in parameter structures from variables in the stack frame of the subroutine.

     Parameter  Description
  1  $sub       Sub definition
  2  $sv        Structure variables
  3  $S         Source tree of input structures
  4  @P         Path through source structures tree

=head2 Nasm::X86::Subroutine::uploadToNewStackFrame($sub, $sv, $source, $target)

Map a variable in the current stack into a reference in the next stack frame being the one that will be used by this sub.

     Parameter  Description
  1  $sub       Subroutine descriptor
  2  $sv        Structure variables
  3  $source    Source variable in the current stack frame
  4  $target    The reference in the new stack frame

=head2 Nasm::X86::Subroutine::validateParameters($sub, %options)

Check that the parameters and structures presented in a call to a subroutine math those defined for the subroutine.

     Parameter  Description
  1  $sub       Subroutine descriptor
  2  %options   Options

=head2 PrintTraceBack($channel)

Trace the call stack.

     Parameter  Description
  1  $channel   Channel to write on

=head2 PrintErrTraceBack($message)

Print sub routine track back on stderr and then exit with a message.

     Parameter  Description
  1  $message   Reason why we are printing the trace back and then stopping

=head2 PrintNL($channel)

Print a new line to stdout  or stderr.

     Parameter  Description
  1  $channel   Channel to write on

=head2 PrintErrNL()

Print a new line to stderr.


=head2 PrintString($channel, @string)

Print a constant string to the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  @string    Strings

=head2 PrintStringNL($channel, @string)

Print a constant string to the specified channel followed by a new line.

     Parameter  Description
  1  $channel   Channel
  2  @string    Strings

=head2 PrintErrString(@string)

Print a constant string to stderr.

     Parameter  Description
  1  @string    String

=head2 PrintErrStringNL(@string)

Print a constant string to stderr followed by a new line.

     Parameter  Description
  1  @string    String

B<Example:>


    PrintOutStringNL "Hello World";
    PrintOutStringNL "Hello
World";

    PrintErrStringNL "Hello World";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    ok Assemble eq => <<END, avx512=>0, label=>'t1';
  Hello World
  Hello
  World
  END


=head2 PrintCString($channel, $string)

Print a zero terminated C style string addressed by a variable on the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $string    String

=head2 PrintCStringNL($channel, $string)

Print a zero terminated C style string addressed by a variable on the specified channel followed by a new line.

     Parameter  Description
  1  $channel   Channel
  2  $string    Strings

=head2 PrintSpace($channel, $spaces)

Print a constant number of spaces to the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $spaces    Number of spaces if not one.

=head2 PrintErrSpace($spaces)

Print  a constant number of spaces to stderr.

     Parameter  Description
  1  $spaces    Number of spaces if not one.

=head2 hexTranslateTable()

Create/address a hex translate table and return its label.


=head2 PrintRaxInHex($channel, $end)

Write the content of register rax in hexadecimal in big endian notation to the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $end       Optional end byte

=head2 PrintErrRaxInHex()

Write the content of register rax in hexadecimal in big endian notation to stderr.


=head2 PrintErrRaxInHexNL()

Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.


=head2 PrintRax_InHex($channel, $end)

Write the content of register rax in hexadecimal in big endian notation to the specified channel replacing zero bytes with __.

     Parameter  Description
  1  $channel   Channel
  2  $end       Optional end byte

=head2 PrintErrRax_InHex()

Write the content of register rax in hexadecimal in big endian notation to stderr.


=head2 PrintErrRax_InHexNL()

Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.


=head2 PrintOneRegisterInHex($channel, $r)

Print the named register as a hex string.

     Parameter  Description
  1  $channel   Channel to print on
  2  $r         Register to print

=head2 PrintErrOneRegisterInHex($r)

Print the named register as a hex string on stderr.

     Parameter  Description
  1  $r         Register to print

=head2 PrintErrOneRegisterInHexNL($r)

Print the named register as a hex string on stderr followed by new line.

     Parameter  Description
  1  $r         Register to print

=head2 PrintRegisterInHex($channel, @r)

Print the named registers as hex strings.

     Parameter  Description
  1  $channel   Channel to print on
  2  @r         Names of the registers to print

=head2 PrintErrRegisterInHex(@r)

Print the named registers as hex strings on stderr.

     Parameter  Description
  1  @r         Names of the registers to print

=head2 PrintOutRipInHex()

Print the instruction pointer in hex.


=head2 PrintOutRflagsInHex()

Print the flags register in hex.


=head2 PrintErrZF()

Print the zero flag without disturbing it on stderr.


=head2 PrintRightInHex($channel, $number, $width)

Print out a number in hex right justified in a field of specified width on the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $number    Number as a variable or register
  3  $width     Width of output field as a constant

=head2 PrintErrRightInHex($number, $width)

Write the specified variable in hexadecimal right justified in a field of specified width on stderr.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

=head2 PrintErrRightInHexNL($number, $width)

Write the specified variable in hexadecimal right justified in a field of specified width on stderr followed by a new line.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

=head2 PrintRightInBin($channel, $Number, $Width)

Print out a number in binary right justified in a field of specified width on the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $Number    Number as a variable or register
  3  $Width     Width of output field as a variable or constant

=head2 PrintErrRightInBin($number, $width)

Write the specified variable in binary right justified in a field of specified width on stderr.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

=head2 PrintErrRightInBinNL($number, $width)

Write the specified variable in binary right justified in a field of specified width on stderr followed by a new line.

     Parameter  Description
  1  $number    Number as a variable
  2  $width     Width of output field as a variable

=head2 PrintRaxInDec($channel)

Print rax in decimal on the specified channel.

     Parameter  Description
  1  $channel   Channel to write on

=head2 PrintErrRaxInDec()

Print rax in decimal on stderr.


=head2 PrintErrRaxInDecNL()

Print rax in decimal on stderr followed by a new line.


=head2 PrintRaxRightInDec($Width, $channel)

Print rax in decimal right justified in a field of the specified width on the specified channel.

     Parameter  Description
  1  $Width     Width as a variable or a constant
  2  $channel   Channel

=head2 PrintRightInDec($channel, $Number, $width)

Print out a number in decimal right justified in a field of specified width on the specified channel.

     Parameter  Description
  1  $channel   Channel
  2  $Number    Number as a variable or register
  3  $width     Width of output field as a variable or constant

=head2 PrintErrRightInDec($number, $width)

Print a variable or register in decimal right justified in a field of the specified width on stderr.

     Parameter  Description
  1  $number    Number as a variable or a register
  2  $width     Width as a variable or constant

=head2 PrintErrRightInDecNL($number, $width)

Print a variable or register in decimal right justified in a field of the specified width on stderr followed by a new line.

     Parameter  Description
  1  $number    Number as a variable or a register
  2  $width     Width as a variable or constant

=head2 PrintRaxAsText($channel)

Print the string in rax on the specified channel.

     Parameter  Description
  1  $channel   Channel to write on

=head2 PrintErrRaxAsText()

Print rax as text on stderr.


=head2 PrintErrRaxAsTextNL()

Print rax as text on stderr followed by a new line.


=head2 PrintRaxAsChar($channel)

Print the ascii character in rax on the specified channel.

     Parameter  Description
  1  $channel   Channel to write on

=head2 PrintErrRaxAsChar()

Print the character in rax on stderr.


=head2 PrintErrRaxAsCharNL()

Print the character in rax on stderr followed by a new line.


=head2 Variable($name, $expr, %options)

Create a new variable with the specified name initialized via an optional expression.

     Parameter  Description
  1  $name      Name of variable
  2  $expr      Optional expression initializing variable
  3  %options   Options

=head2 R($name)

Define a reference variable.

     Parameter  Description
  1  $name      Name of variable

=head2 Nasm::X86::Variable::dump($left, $channel, $newLine, $title1, $title2)

Dump the value of a variable to the specified channel adding an optional title and new line if requested.

     Parameter  Description
  1  $left      Left variable
  2  $channel   Channel
  3  $newLine   New line required
  4  $title1    Optional leading title
  5  $title2    Optional trailing title

B<Example:>


    PrintCString  ($stdout, V(str => Rs("abc\0def")));
    PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));
    ok Assemble eq => <<END;
  abcABC
  END

    my $a = V(a => 3);  $a->outNL;
    my $b = K(b => 2);  $b->outNL;
    my $c = $a +  $b; $c->outNL;
    my $d = $c -  $a; $d->outNL;
    my $g = $a *  $b; $g->outNL;
    my $h = $g /  $b; $h->outNL;
    my $i = $a %  $b; $i->outNL;

    If ($a == 3,
    Then
     {PrintOutStringNL "a == 3"
     },
    Else
     {PrintOutStringNL "a != 3"
     });

    ++$a; $a->outNL;
    --$a; $a->outNL;

    ok Assemble eq => <<END, avx512=>0;
  a: .... .... .... ...3
  b: .... .... .... ...2
  (a add b): .... .... .... ...5
  ((a add b) sub a): .... .... .... ...2
  (a times b): .... .... .... ...6
  ((a times b) / b): .... .... .... ...3
  (a % b): .... .... .... ...1
  a == 3
  a: .... .... .... ...4
  a: .... .... .... ...3
  END


=head2 Nasm::X86::Variable::err($left, $title1, $title2)

Dump the value of a variable on stderr.

     Parameter  Description
  1  $left      Left variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

=head2 Nasm::X86::Variable::errNL($left, $title1, $title2)

Dump the value of a variable on stderr and append a new line.

     Parameter  Description
  1  $left      Left variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

=head2 Nasm::X86::Variable::d($left, $title1, $title2)

Dump the value of a variable on stderr and append the source file calling line in a format that Geany understands.

     Parameter  Description
  1  $left      Left variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

=head2 Nasm::X86::Variable::errInDec($number, $title1, $title2)

Dump the value of a variable on stderr in decimal.

     Parameter  Description
  1  $number    Number as variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

=head2 Nasm::X86::Variable::errInDecNL($number, $title1, $title2)

Dump the value of a variable on stderr in decimal followed by a new line.

     Parameter  Description
  1  $number    Number as variable
  2  $title1    Optional leading title
  3  $title2    Optional trailing title

=head2 Nasm::X86::Variable::rightInDec($number, $channel, $width)

Dump the value of a variable on the specified channel as a decimal  number right adjusted in a field of specified width.

     Parameter  Description
  1  $number    Number as variable
  2  $channel   Channel
  3  $width     Width

=head2 Nasm::X86::Variable::errRightInDec($number, $width)

Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width.

     Parameter  Description
  1  $number    Number
  2  $width     Width

=head2 Nasm::X86::Variable::errRightInDecNL($number, $width)

Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width followed by a new line.

     Parameter  Description
  1  $number    Number
  2  $width     Width

=head2 Nasm::X86::Variable::rightInHex($number, $channel, $width)

Write the specified variable number in hexadecimal right justified in a field of specified width to the specified channel.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $channel   Channel to print on
  3  $width     Width of output field

=head2 Nasm::X86::Variable::errRightInHex($number, $width)

Write the specified variable number in hexadecimal right justified in a field of specified width to stderr.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

=head2 Nasm::X86::Variable::errRightInHexNL($number, $width)

Write the specified variable number in hexadecimal right justified in a field of specified width to stderr followed by a new line.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

=head2 Nasm::X86::Variable::rightInBin($number, $channel, $width)

Write the specified variable number in binary right justified in a field of specified width to the specified channel.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $channel   Channel to print on
  3  $width     Width of output field

=head2 Nasm::X86::Variable::errRightInBin($number, $width)

Write the specified variable number in binary right justified in a field of specified width to stderr.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

=head2 Nasm::X86::Variable::errRightInBinNL($number, $width)

Write the specified variable number in binary right justified in a field of specified width to stderr followed by a new line.

     Parameter  Description
  1  $number    Number to print as a variable
  2  $width     Width of output field

=head2 Nasm::X86::Variable::errSpaces($count)

Print the specified number of spaces to stderr.

     Parameter  Description
  1  $count     Number of spaces

=head2 Nasm::X86::Variable::errCString($string)

Print a zero terminated C style string addressed by a variable on stderr.

     Parameter  Description
  1  $string    String

=head2 Nasm::X86::Variable::errCStringNL($string)

Print a zero terminated C style string addressed by a variable on stderr followed by a new line.

     Parameter  Description
  1  $string    String

=head2 Nasm::X86::Variable::equals($op, $left, $right)

Equals operator.

     Parameter  Description
  1  $op        Operator
  2  $left      Left variable
  3  $right     Right variable

=head2 Nasm::X86::Variable::assign($left, $op, $right)

Assign to the left hand side the value of the right hand side.

     Parameter  Description
  1  $left      Left variable
  2  $op        Operator
  3  $right     Right variable

=head2 Nasm::X86::Variable::plusAssign($left, $right)

Implement plus and assign.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::minusAssign($left, $right)

Implement minus and assign.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::arithmetic($op, $name, $left, $right)

Return a variable containing the result of an arithmetic operation on the left hand and right hand side variables.

     Parameter  Description
  1  $op        Operator
  2  $name      Operator name
  3  $left      Left variable
  4  $right     Right variable

=head2 Nasm::X86::Variable::add($left, $right)

Add the right hand variable to the left hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::sub($left, $right)

Subtract the right hand variable from the left hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::times($left, $right)

Multiply the left hand variable by the right hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::division($op, $left, $right)

Return a variable containing the result or the remainder that occurs when the left hand side is divided by the right hand side.

     Parameter  Description
  1  $op        Operator
  2  $left      Left variable
  3  $right     Right variable

=head2 Nasm::X86::Variable::divide($left, $right)

Divide the left hand variable by the right hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::mod($left, $right)

Divide the left hand variable by the right hand variable and return the remainder as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::shiftLeft($left, $right)

Shift the left hand variable left by the number of bits specified in the right hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

B<Example:>


    K(loop=>16)->for(sub
     {my ($index, $start, $next, $end) = @_;
     (K(one => 1)     << $index)->outRightInBinNL(16);
     (K(one => 1<<15) >> $index)->outRightInBinNL(16);
     });

    ok Assemble eq => <<END, avx512=>1;
                 1
  1000000000000000
                10
   100000000000000
               100
    10000000000000
              1000
     1000000000000
             10000
      100000000000
            100000
       10000000000
           1000000
        1000000000
          10000000
         100000000
         100000000
          10000000
        1000000000
           1000000
       10000000000
            100000
      100000000000
             10000
     1000000000000
              1000
    10000000000000
               100
   100000000000000
                10
  1000000000000000
                 1
  END


=head2 Nasm::X86::Variable::shiftRight($left, $right)

Shift the left hand variable right by the number of bits specified in the right hand variable and return the result as a new variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

B<Example:>


    K(loop=>16)->for(sub
     {my ($index, $start, $next, $end) = @_;
     (K(one => 1)     << $index)->outRightInBinNL(16);
     (K(one => 1<<15) >> $index)->outRightInBinNL(16);
     });

    ok Assemble eq => <<END, avx512=>1;
                 1
  1000000000000000
                10
   100000000000000
               100
    10000000000000
              1000
     1000000000000
             10000
      100000000000
            100000
       10000000000
           1000000
        1000000000
          10000000
         100000000
         100000000
          10000000
        1000000000
           1000000
       10000000000
            100000
      100000000000
             10000
     1000000000000
              1000
    10000000000000
               100
   100000000000000
                10
  1000000000000000
                 1
  END


=head2 Nasm::X86::Variable::not($left)

Form two complement of left hand side and return it as a variable.

     Parameter  Description
  1  $left      Left variable

=head2 Nasm::X86::Variable::booleanZF($sub, $op, $left, $right)

Combine the left hand variable with the right hand variable via a boolean operator and indicate the result by setting the zero flag if the result is true.

     Parameter  Description
  1  $sub       Operator
  2  $op        Operator name
  3  $left      Left variable
  4  $right     Right variable

=head2 Nasm::X86::Variable::eq($left, $right)

Check whether the left hand variable is equal to the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::ne($left, $right)

Check whether the left hand variable is not equal to the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::ge($left, $right)

Check whether the left hand variable is greater than or equal to the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::gt($left, $right)

Check whether the left hand variable is greater than the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::le($left, $right)

Check whether the left hand variable is less than or equal to the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::lt($left, $right)

Check whether the left hand variable is less than the right hand variable.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::incDec($left, $op)

Increment or decrement a variable.

     Parameter  Description
  1  $left      Left variable operator
  2  $op        Address of operator to perform inc or dec

=head2 Nasm::X86::Variable::inc($left)

Increment a variable.

     Parameter  Description
  1  $left      Variable

=head2 Nasm::X86::Variable::dec($left)

Decrement a variable.

     Parameter  Description
  1  $left      Variable

=head2 Nasm::X86::Variable::str($left)

The name of the variable.

     Parameter  Description
  1  $left      Variable

B<Example:>


  if (1)
   {my $a = V(key => 0x123);
    is_deeply $a->at,           "[rbp-8*(2)]";
    is_deeply $a->addressExpr, "[rbp-8*(2)]";

    ok !$a->isRef;

    Mov rax, -1;
    Kmovq k1, rax;
    PrintOutRegisterInHex k1;

    K(key => 2)->clearBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 2)->setBit(rax);
    PrintOutRegisterInHex rax;

    K(key => 3)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->setMaskBit(k1);
    PrintOutRegisterInHex k1;

    K(key => 0)->clearMaskBit(k1);
    PrintOutRegisterInHex k1;

    ClearRegisters k1;
    K(key => 7)->setMaskFirst(k1);
    PrintOutRegisterInHex k1;

    K(key => 3)->spaces($stdout);
    PrintOutRegisterInHex k1;

    my $N = K size => 4096;
    my $A = $N->allocateMemory;
    $A->clearMemory($N);
    ClearMemory($A, $N);

    $A->setReg(rax);
    Mov "dword[rax]",   0x61626364;
    Mov "dword[rax+4]", 0x65666768;

    ($A+8)->copyMemory($A, K key => 8);

    $A->printOutMemory  (K(key => 16));
    $A->printOutMemoryNL(K(key => 16));

    K(K => Rd(0..63))->loadZmm(1);
    PrintOutRegisterInHex zmm1;

    ok Assemble eq => <<END;
      k1: .... .... .... ..-1
     rax: FFFF FFFF FFFF FFFB
     rax: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFF7
      k1: .... .... .... ..-1
      k1: FFFF FFFF FFFF FFFE
      k1: .... .... .... ..7F
         k1: .... .... .... ..7F
  dcbahgfedcbahgfedcbahgfedcbahgfe
    zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
  END
   }


=head2 Nasm::X86::Variable::and($left, $right)

And two variables.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::or($left, $right)

Or two variables.

     Parameter  Description
  1  $left      Left variable
  2  $right     Right variable

=head2 Nasm::X86::Variable::putBwdqIntoMm($content, $size, $mm, $offset)

Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register.

     Parameter  Description
  1  $content   Variable with content
  2  $size      Size of put
  3  $mm        Numbered zmm
  4  $offset    Offset in bytes

=head2 dFromPointInZ($point, $zmm, %options)

Get the double word from the numbered zmm register at a point specified by the variable or register and return it in a variable.

     Parameter  Description
  1  $point     Point
  2  $zmm       Numbered zmm
  3  %options   Options

B<Example:>


    Mov r15, 0x12345678;
    bRegIntoZmm(r15, 1,  0);
    bRegIntoZmm(r15, 1,  1);
    dRegIntoZmm(r15, 1,  4);
    dRegIntoZmm(r15, 1,  8);
    dRegIntoZmm(r15, 1, 12);
    PrintOutRegisterInHex xmm(1);
    PrintOutRegisterInHex zmm(1);

    bRegFromZmm(r15, 1, 1);
    PrintOutRegisterInHex r15;

    bFromX(1, 0)->outNL;
    bFromZ(1, 0)->outNL;

    dRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    wRegFromZmm(r14, 1, 3);
    PrintOutRegisterInHex r14;

    bFromX(1, 0)->outNL;
    bFromZ(1, 1)->outNL;
    bFromZ(1, 2)->outNL;

    wFromX(1, 0)->outNL;
    wFromZ(1, 1)->outNL;
    wFromZ(1, 2)->outNL;

    dFromX(1, 0)->outNL;
    dFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;

    qFromX(1, 0)->outNL;
    qFromZ(1, 1)->outNL;
    dFromZ(1, 2)->outNL;


    K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    Mov       r15, 2;
    Kmovq k7, r15;
    LoadZmm 2, map {0xff} 1..64;
    PrintOutRegisterInHex zmm2;
    Vmovdqu8 zmmM (2, 7), zmm(1);
    Vmovdqu8 zmmMZ(3, 7), zmm(1);
    PrintOutRegisterInHex zmm1, zmm2, zmm3;

    ok Assemble eq => <<END;
    xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
     r15: .... .... 1234 5678
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 0 in zmm1: .... .... .... ..78
     r14: .... .... 3456 78..
     r14: .... .... 3456 78..
  b at offset 0 in xmm1: .... .... .... ..78
  b at offset 1 in zmm1: .... .... .... ..78
  b at offset 2 in zmm1: .... .... .... ...0
  w at offset 0 in xmm1: .... .... .... 7878
  w at offset 1 in zmm1: .... .... .... ..78
  w at offset 2 in zmm1: .... .... .... ...0
  d at offset 0 in xmm1: .... .... .... 7878
  d at offset 1 in zmm1: .... .... 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  q at offset 0 in xmm1: 1234 5678 .... 7878
  q at offset 1 in zmm1: 7812 3456 78.. ..78
  d at offset 2 in zmm1: .... .... 5678 ....
  d: .... .... .... ...0
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
    zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
    zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
    zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
  END


=head2 Nasm::X86::Variable::printMemory($address, $channel, $size)

Print the specified number of bytes from the memory addressed by the variable on the specified channel.

     Parameter  Description
  1  $address   Address of memory
  2  $channel   Channel to print on as a constant
  3  $size      Number of bytes to print

=head2 Nasm::X86::Variable::printErrMemory($address, $size)

Print the specified number of bytes of the memory addressed by the variable on stdout.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

=head2 Nasm::X86::Variable::printErrMemoryNL($address, $size)

Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

=head2 Nasm::X86::Variable::printMemoryInHexNL($address, $channel, $size)

Write, in hexadecimal, the memory addressed by a variable to stdout or stderr.

     Parameter  Description
  1  $address   Address of memory
  2  $channel   Channel to print on
  3  $size      Number of bytes to print

=head2 Nasm::X86::Variable::printErrMemoryInHexNL($address, $size)

Write the memory addressed by a variable to stderr.

     Parameter  Description
  1  $address   Address of memory
  2  $size      Number of bytes to print

=head2 PrintMemoryInHex($channel)

Dump memory from the address in rax for the length in rdi on the specified channel. As this method prints in blocks of 8 up to 7 bytes will be missing from the end unless the length is a multiple of 8 .

     Parameter  Description
  1  $channel   Channel

=head2 PrintErrMemoryInHex()

Dump memory from the address in rax for the length in rdi on stderr.


=head2 PrintErrMemoryInHexNL()

Dump memory from the address in rax for the length in rdi and then print a new line.


=head2 PrintMemory_InHex($channel)

Dump memory from the address in rax for the length in rdi on the specified channel. As this method prints in blocks of 8 up to 7 bytes will be missing from the end unless the length is a multiple of 8 .

     Parameter  Description
  1  $channel   Channel

=head2 PrintErrMemory_InHex()

Dump memory from the address in rax for the length in rdi on stderr.


=head2 PrintErrMemory_InHexNL()

Dump memory from the address in rax for the length in rdi and then print a new line.


=head2 PrintMemory($channel)

Print the memory addressed by rax for a length of rdi on the specified channel where channel can be a constant number or a register expression using a bound register.

     Parameter  Description
  1  $channel   Channel

B<Example:>


  if (1)
   {my $s = "zzzCreated.data";
    my $f = Rs $s;
    Mov rax, $f;
    OpenWrite;
    Mov r15, rax;
    Mov rax, $f;
    Mov rdi, length $s;

    PrintMemory r15;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    CloseFile;
    ok Assemble eq=><<END, avx512=>1;
  END
    ok -e $s;
    unlink $s;
   }

  if (!!onGitHub) {


=head2 PrintMemoryNL()

Print the memory addressed by rax for a length of rdi on the specified channel followed by a new line.


=head2 PrintErrMemory()

Print the memory addressed by rax for a length of rdi on stderr.


=head2 PrintErrMemoryNL()

Print the memory addressed by rax for a length of rdi followed by a new line on stderr.


=head2 CopyMemory4K($source, $target, $size)

Copy memory in 4K byte blocks.

     Parameter  Description
  1  $source    Source address variable
  2  $target    Target address variable
  3  $size      Number of 4K byte blocks to move

=head2 convert_rax_from_utf32_to_utf8()

Convert a utf32 character held in rax to a utf8 character held in rax.


B<Example:>


  # $ 	U+0024                 010 0100                00100100                     24
  # £ 	U+00A3 	          000 1010 0011                11000010 10100011            C2 A3
  # ह 	  U+0939    	0000 1001 0011 1001                11100000 10100100 10111001   E0 A4 B9
  # € 	U+20AC    	0010 0000 1010 1100                11100010 10000010 10101100   E2 82 AC
  # 한 	U+D55C     	1101 0101 0101 1100                11101101 10010101 10011100   ED 95 9C
  # 𐍈   	U+10348 	0 0001 0000 0011 0100 1000 	11110000 10010000 10001101 10001000   F0 90 8D 88
    Mov rax, 0x40;                                                                # 0x40

    convert_rax_from_utf32_to_utf8;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    Mov rax, 0x03b1;                                                              # 0xCE 0xB1

    convert_rax_from_utf32_to_utf8;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    Mov rax, 0x20ac;                                                              # 0xE2 0x82 0xAC;

    convert_rax_from_utf32_to_utf8;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    Mov rax, 0x10348;                                                             # 0xf0 0x90 0x8d 0x88

    convert_rax_from_utf32_to_utf8;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;

    ok Assemble eq => <<END, avx512=>1;
     rax: .... .... .... ..40
     rax: .... .... .... B1CE
     rax: .... .... ..AC 82E2
     rax: .... .... 888D 90F0
  END


=head2 Cstrlen()

Length of the C style string addressed by rax returning the length in r15.


=head2 DescribeArea(%options)

Describe a relocatable area.  By describing an areas, we allocate space with which to describe it in the current stack frame but we do not allocate memory for the area itself in the heap.

     Parameter  Description
  1  %options   Optional variable addressing the start of the area

=head2 Nasm::X86::Area::copyDescriptor($target, $source)

Copy the description of one area into another

     Parameter  Description
  1  $target    Target area
  2  $source    Source area

=head2 Nasm::X86::Area::updateSpace($area, $size)

Make sure that a variable addressed area has enough space to accommodate content of a variable size.

     Parameter  Description
  1  $area      Area descriptor
  2  $size      Variable size needed

=head2 Nasm::X86::Area::allocZmmBlock3($area)

Allocate three zmm blocks in one go and return their offsets.

     Parameter  Description
  1  $area      Area

=head2 Nasm::X86::Yggdrasil::UniqueStrings        {K key => 0}(sub Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1})

A tree of strings that assigns unique numbers to strings.

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1}  P Translates a string number into the offset of a subroutine in an area.

=head2 Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1}(sub Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2})

Translates a string number into the offset of a subroutine in an area.

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2}  P Maps the unique string number for a subroutine name to the offset in the are that contains the length (as a dword) followed by the string content of the Perl data structure describing the subroutine in question.

=head2 Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2}(sub Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3})

Maps the unique string number for a subroutine name to the offset in the are that contains the length (as a dword) followed by the string content of the Perl data structure describing the subroutine in question.

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3}  P Unisyn alphabets.

=head2 Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3}(sub Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4})

Unisyn alphabets.

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4}  P Open bracket to close bracket

=head2 Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4}(sub Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5})

Open bracket to close bracket

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5}  P Close bracket to open bracket

=head2 Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5}(sub Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6})

Close bracket to open bracket

     Parameter                                                    Description
  1  sub Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6}  P Permissible transitions from alphabet to alphabet

=head2 Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6}()

Permissible transitions from alphabet to alphabet


=head2 Nasm::X86::Area::checkYggdrasilCreated($area)

Return a tree descriptor for the Yggdrasil world tree for an area. If Yggdrasil has not been created the found field of the returned descriptor will have zero in it else one.

     Parameter  Description
  1  $area      Area descriptor

B<Example:>


    my $A = CreateArea;
    my $t = $A->checkYggdrasilCreated;
       $t->found->outNL;
    my $y = $A->yggdrasil;
    my $T = $A->checkYggdrasilCreated;
       $T->found->outNL;
    ok Assemble debug => 0, eq => <<END, avx512=>1;
  found  : .... .... .... ...0
  found  : .... .... .... ...1
  END


=head2 Nasm::X86::Area::zero($area)

Append a trailing zero to the area addressed by rax.

     Parameter  Description
  1  $area      Area descriptor

=head2 DescribeTree(%options)

Return a descriptor for a tree with the specified options.  The options from one tree get inherited by any sub trees they contain

     Parameter  Description
  1  %options   Tree description options

=head2 Nasm::X86::Area::DescribeTree($area, %options)

Return a descriptor for a tree in the specified area with the specified options.

     Parameter  Description
  1  $area      Area descriptor
  2  %options   Options for tree

=head2 Nasm::X86::Tree::DescribeTree($tree, %options)

Create a description of a tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  %options   {first=>first node of tree if not the existing first node; area=>area used by tree if not the existing area}

=head2 Nasm::X86::Tree::position($tree, $first)

Create a new tree description for a tree positioned at the specified location.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $first     Offset of tree

=head2 Nasm::X86::Tree::firstFromMemory($tree, $zmm)

Load the first block for a tree into the numbered zmm.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm to contain first block

=head2 Nasm::X86::Tree::firstIntoMemory($tree, $zmm)

Save the first block of a tree in the numbered zmm back into memory.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block

=head2 Nasm::X86::Tree::rootIntoFirst($tree, $zmm, $value)

Put the contents of a variable into the root field of the first block of a tree when held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block
  3  $value     Variable containing value to put

=head2 Nasm::X86::Tree::rootFromFirst($tree, $zmm, %options)

Return a variable containing the offset of the root block of a tree from the first block when held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block
  3  %options   Options

=head2 Nasm::X86::Tree::root($t, $F, $offset)

Check whether the specified offset refers to the root of a tree when the first block is held in a zmm register. The result is returned by setting the zero flag to one if the offset is the root, else to zero.

     Parameter  Description
  1  $t         Tree descriptor
  2  $F         Zmm register holding first block
  3  $offset    Offset of block as a variable

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = $t->allocBlock(31, 30, 29);
    K(data => 0x33)->dIntoZ(31, 4);
    $t->lengthIntoKeys(31, K length =>0x9);
    $t->putBlock($b, 31, 30, 29);
    $t->getBlock($b, 25, 24, 23);
    PrintOutRegisterInHex 25;
    $t->lengthFromKeys(25)->outNL;


    $t->firstFromMemory(28);
    $t->incSizeInFirst (28);
    $t->rootIntoFirst  (28, K value => 0x2222);
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->root           (28, K value => 0x2221);  PrintOutZF;
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->firstIntoMemory(28);

    $t->first->outNL;
    $b->outNL;
    $a->dump("1111");
    PrintOutRegisterInHex 31, 30, 29, 28;

    If $t->leafFromNodes(29) > 0, Then {PrintOutStringNL "29 Leaf"}, Else {PrintOutStringNL "29 Branch"};
    If $t->leafFromNodes(28) > 0, Then {PrintOutStringNL "28 Leaf"}, Else {PrintOutStringNL "28 Branch"};


    ok Assemble eq => <<END, avx512=>1;
   zmm25: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
  b at offset 56 in zmm25: .... .... .... ...9
  ZF=1
  ZF=0
  ZF=1
  first  : .... .... .... ..40
  address: .... .... .... ..80
  1111
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 2222 ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ 33__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .9__ ____ C0__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
   zmm31: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
   zmm30: .... .1.. .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm29: .... ..40 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm28: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... 2222
  29 Leaf
  28 Branch
  END


=head2 Nasm::X86::Tree::optionsFromFirst($tree, $zmm, %options)

Return a variable containing the options double word from the first block zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block
  3  %options   Options

=head2 Nasm::X86::Tree::optionsIntoFirst($tree, $zmm, $value, %options)

Put the contents of a variable into the options field of the first block of a tree  when the first block is held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block
  3  $value     Variable containing options to put
  4  %options   Options

=head2 Nasm::X86::Tree::sizeFromFirst($tree, $zmm)

Return a variable containing the number of keys in the specified tree when the first block is held in a zmm register..

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block

=head2 Nasm::X86::Tree::sizeIntoFirst($tree, $zmm, $value)

Put the contents of a variable into the size field of the first block of a tree  when the first block is held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block
  3  $value     Variable containing value to put

=head2 Nasm::X86::Tree::incSizeInFirst($tree, $zmm)

Increment the size field in the first block of a tree when the first block is held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block

B<Example:>


    my $t = DescribeTree();

    my $F = 31;
    K(key => 0xff)->dIntoZ($F, 60);
    K(key => 0xee)->dIntoZ($F, 56);
    PrintOutRegisterInHex $F;
    $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
    $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
    $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
    $t->decSizeInFirst($F); PrintOutRegisterInHex $F;
    $t->decSizeInFirst($F); PrintOutRegisterInHex $F;
    $t->decSizeInFirst($F); PrintOutRegisterInHex $F;

    ok Assemble eq => <<END, avx512=>1;                                           # Once we know the insertion point we can add the key/data/subTree triple, increase the length and update the tree bits
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...2  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...3  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...2  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... ...0
   zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::decSizeInFirst($tree, $zmm)

Decrement the size field in the first block of a tree when the first block is held in a zmm register.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing first block

=head2 Nasm::X86::Tree::incSize($tree)

Increment the size of a tree.

     Parameter  Description
  1  $tree      Tree descriptor

=head2 Nasm::X86::Tree::decSize($tree)

Decrement the size of a tree.

     Parameter  Description
  1  $tree      Tree descriptor

=head2 Nasm::X86::Tree::allocBlock($tree, $K, $D, $N)

Allocate a keys/data/node block and place it in the numbered zmm registers.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $K         Numbered zmm for keys
  3  $D         Numbered zmm for data
  4  $N         Numbered zmm for children

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = $t->allocBlock(31, 30, 29);
    K(data => 0x33)->dIntoZ(31, 4);
    $t->lengthIntoKeys(31, K length =>0x9);
    $t->putBlock($b, 31, 30, 29);
    $t->getBlock($b, 25, 24, 23);
    PrintOutRegisterInHex 25;
    $t->lengthFromKeys(25)->outNL;


    $t->firstFromMemory(28);
    $t->incSizeInFirst (28);
    $t->rootIntoFirst  (28, K value => 0x2222);
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->root           (28, K value => 0x2221);  PrintOutZF;
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->firstIntoMemory(28);

    $t->first->outNL;
    $b->outNL;
    $a->dump("1111");
    PrintOutRegisterInHex 31, 30, 29, 28;

    If $t->leafFromNodes(29) > 0, Then {PrintOutStringNL "29 Leaf"}, Else {PrintOutStringNL "29 Branch"};
    If $t->leafFromNodes(28) > 0, Then {PrintOutStringNL "28 Leaf"}, Else {PrintOutStringNL "28 Branch"};


    ok Assemble eq => <<END, avx512=>1;
   zmm25: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
  b at offset 56 in zmm25: .... .... .... ...9
  ZF=1
  ZF=0
  ZF=1
  first  : .... .... .... ..40
  address: .... .... .... ..80
  1111
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 2222 ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ 33__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .9__ ____ C0__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
   zmm31: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
   zmm30: .... .1.. .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm29: .... ..40 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm28: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... 2222
  29 Leaf
  28 Branch
  END


=head2 Nasm::X86::Tree::freeBlock($tree, $k, $K, $D, $N)

Free a keys/data/node block whose keys  block entry is located at the specified offset.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $k         Offset of keys block
  3  $K         Numbered zmm for keys
  4  $D         Numbered zmm for data
  5  $N         Numbered zmm for children

=head2 Nasm::X86::Tree::upFromData($tree, $zmm, %options)

Up from the data zmm in a block in a tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing data block
  3  %options   Options

=head2 Nasm::X86::Tree::upIntoData($tree, $value, $zmm)

Up into the data zmm in a block in a tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $value     Variable containing value to put
  3  $zmm       Number of zmm containing first block

=head2 Nasm::X86::Tree::lengthFromKeys($t, $zmm, %options)

Get the length of the keys block in the numbered zmm and return it as a variable.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Zmm number
  3  %options   Options

=head2 Nasm::X86::Tree::lengthIntoKeys($t, $zmm, $length)

Get the length of the block in the numbered zmm from the specified variable.

     Parameter  Description
  1  $t         Tree
  2  $zmm       Zmm number
  3  $length    Length variable

=head2 Nasm::X86::Tree::incLengthInKeys($t, $K)

Increment the number of keys in a keys block or complain if such is not possible.

     Parameter  Description
  1  $t         Tree
  2  $K         Zmm number

=head2 Nasm::X86::Tree::decLengthInKeys($t, $K)

Decrement the number of keys in a keys block or complain if such is not possible.

     Parameter  Description
  1  $t         Tree
  2  $K         Zmm number

=head2 Nasm::X86::Tree::leafFromNodes($tree, $zmm, %options)

Return a variable containing true if we are on a leaf.  We determine whether we are on a leaf by checking the offset of the first sub node.  If it is zero we are on a leaf otherwise not.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $zmm       Number of zmm containing node block
  3  %options   Options

=head2 Nasm::X86::Tree::getLoop($t, $zmm, %options)

Return the value of the loop field as a variable.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  %options   Options

=head2 Nasm::X86::Tree::putLoop($t, $value, $zmm)

Set the value of the loop field from a variable.

     Parameter  Description
  1  $t         Tree descriptor
  2  $value     Variable containing offset of next loop entry
  3  $zmm       Numbered zmm

=head2 Nasm::X86::Tree::maskForFullKeyArea()

Place a mask for the full key area in the numbered mask register.


=head2 Nasm::X86::Tree::maskForFullNodesArea()

Place a mask for the full nodes area in the numbered mask register.


=head2 Nasm::X86::Tree::getBlock($tree, $offset, $K, $D, $N)

Get the keys, data and child nodes for a tree node from the specified offset in the area for the tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $offset    Offset of block as a variable
  3  $K         Numbered zmm for keys
  4  $D         Numbered data for keys
  5  $N         Numbered zmm for nodes

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = $t->allocBlock(31, 30, 29);
    K(data => 0x33)->dIntoZ(31, 4);
    $t->lengthIntoKeys(31, K length =>0x9);
    $t->putBlock($b, 31, 30, 29);
    $t->getBlock($b, 25, 24, 23);
    PrintOutRegisterInHex 25;
    $t->lengthFromKeys(25)->outNL;


    $t->firstFromMemory(28);
    $t->incSizeInFirst (28);
    $t->rootIntoFirst  (28, K value => 0x2222);
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->root           (28, K value => 0x2221);  PrintOutZF;
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->firstIntoMemory(28);

    $t->first->outNL;
    $b->outNL;
    $a->dump("1111");
    PrintOutRegisterInHex 31, 30, 29, 28;

    If $t->leafFromNodes(29) > 0, Then {PrintOutStringNL "29 Leaf"}, Else {PrintOutStringNL "29 Branch"};
    If $t->leafFromNodes(28) > 0, Then {PrintOutStringNL "28 Leaf"}, Else {PrintOutStringNL "28 Branch"};


    ok Assemble eq => <<END, avx512=>1;
   zmm25: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
  b at offset 56 in zmm25: .... .... .... ...9
  ZF=1
  ZF=0
  ZF=1
  first  : .... .... .... ..40
  address: .... .... .... ..80
  1111
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 2222 ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ 33__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .9__ ____ C0__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
   zmm31: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
   zmm30: .... .1.. .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm29: .... ..40 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm28: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... 2222
  29 Leaf
  28 Branch
  END


=head2 Nasm::X86::Tree::putBlock($t, $offset, $K, $D, $N)

Put a tree block held in three zmm registers back into the area holding the tree at the specified offset.

     Parameter  Description
  1  $t         Tree descriptor
  2  $offset    Offset of block as a variable
  3  $K         Numbered zmm for keys
  4  $D         Numbered data for keys
  5  $N         Numbered zmm for nodes

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $b = $t->allocBlock(31, 30, 29);
    K(data => 0x33)->dIntoZ(31, 4);
    $t->lengthIntoKeys(31, K length =>0x9);
    $t->putBlock($b, 31, 30, 29);
    $t->getBlock($b, 25, 24, 23);
    PrintOutRegisterInHex 25;
    $t->lengthFromKeys(25)->outNL;


    $t->firstFromMemory(28);
    $t->incSizeInFirst (28);
    $t->rootIntoFirst  (28, K value => 0x2222);
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->root           (28, K value => 0x2221);  PrintOutZF;
    $t->root           (28, K value => 0x2222);  PrintOutZF;
    $t->firstIntoMemory(28);

    $t->first->outNL;
    $b->outNL;
    $a->dump("1111");
    PrintOutRegisterInHex 31, 30, 29, 28;

    If $t->leafFromNodes(29) > 0, Then {PrintOutStringNL "29 Leaf"}, Else {PrintOutStringNL "29 Branch"};
    If $t->leafFromNodes(28) > 0, Then {PrintOutStringNL "28 Leaf"}, Else {PrintOutStringNL "28 Branch"};


    ok Assemble eq => <<END, avx512=>1;
   zmm25: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
  b at offset 56 in zmm25: .... .... .... ...9
  ZF=1
  ZF=0
  ZF=1
  first  : .... .... .... ..40
  address: .... .... .... ..80
  1111
  Area     Size:     4096    Used:      320
  .... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..40 | 2222 ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
  .... .... .... ..80 | ____ ____ 33__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .9__ ____ C0__ ____
  .... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
   zmm31: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
   zmm30: .... .1.. .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm29: .... ..40 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm28: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... 2222
  29 Leaf
  28 Branch
  END


=head2 Nasm::X86::Tree::firstNode($tree, $K, $D, $N)

Return as a variable the last node block in the specified tree node held in a zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $K         Key zmm
  3  $D         Data zmm
  4  $N         Node zmm for a node block

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))->loadZmm($K);
    K(K => Rd( 1..16))->loadZmm($N);

    $t->lengthIntoKeys($K, K length => $t->length);

    PrintOutRegisterInHex 31, 29;
    my $f = $t->firstNode($K, $D, $N);
    my $l = $t-> lastNode($K, $D, $N);
    $f->outNL;
    $l->outNL;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  d at offset 0 in zmm29: .... .... .... ...1
  d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))->loadZmm($K);
    K(K => Rd( 1..16))->loadZmm($N);

    $t->lengthIntoKeys($K, K length => $t->length);

    PrintOutRegisterInHex 31, 29;
    my $f = $t->firstNode($K, $D, $N);
    my $l = $t-> lastNode($K, $D, $N);
    $f->outNL;
    $l->outNL;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  d at offset 0 in zmm29: .... .... .... ...1
  d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
  END


=head2 Nasm::X86::Tree::lastNode($tree, $K, $D, $N)

Return as a variable the last node block in the specified tree node held in a zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $K         Key zmm
  3  $D         Data zmm
  4  $N         Node zmm for a node block

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))->loadZmm($K);
    K(K => Rd( 1..16))->loadZmm($N);

    $t->lengthIntoKeys($K, K length => $t->length);

    PrintOutRegisterInHex 31, 29;
    my $f = $t->firstNode($K, $D, $N);
    my $l = $t-> lastNode($K, $D, $N);
    $f->outNL;
    $l->outNL;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  d at offset 0 in zmm29: .... .... .... ...1
  d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
  END

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))->loadZmm($K);
    K(K => Rd( 1..16))->loadZmm($N);

    $t->lengthIntoKeys($K, K length => $t->length);

    PrintOutRegisterInHex 31, 29;
    my $f = $t->firstNode($K, $D, $N);
    my $l = $t-> lastNode($K, $D, $N);
    $f->outNL;
    $l->outNL;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
  d at offset 0 in zmm29: .... .... .... ...1
  d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
  END


=head2 Nasm::X86::Tree::relativeNode($tree, $offset, $relative, $K, $N)

Return as a variable a node offset relative (specified as ac constant) to another offset in the same node in the specified zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $offset    Offset
  3  $relative  Relative location
  4  $K         Key zmm
  5  $N         Node zmm

=head2 Nasm::X86::Tree::nextNode($tree, $offset, $K, $N)

Return as a variable the next node block offset after the specified one in the specified zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $offset    Offset
  3  $K         Key zmm
  4  $N         Node zmm

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    K(loop => 66)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->put($index, 2 * $index);
     });
    $t->getBlock(K(offset=>0x200), 31, 30, 29);
    $t->nextNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);
    $t->prevNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);

    ok Assemble eq => <<END, avx512=>1;
  500
  380
  END


=head2 Nasm::X86::Tree::prevNode($tree, $offset, $K, $N)

Return as a variable the previous node block offset after the specified one in the specified zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $offset    Offset
  3  $K         Key zmm
  4  $N         Node zmm

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;

    K(loop => 66)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $t->put($index, 2 * $index);
     });
    $t->getBlock(K(offset=>0x200), 31, 30, 29);
    $t->nextNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);
    $t->prevNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);

    ok Assemble eq => <<END, avx512=>1;
  500
  380
  END


=head2 Nasm::X86::Tree::indexNode($tree, $offset, $K, $N)

Return, as a variable, the point mask obtained by testing the nodes in a block for specified offset. We have to supply the keys as well so that we can find the number of nodes. We need the number of nodes so that we only search the valid area not all possible node positions in the zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $offset    Key as a variable
  3  $K         Zmm containing keys
  4  $N         Comparison from B<Vpcmp>

=head2 Nasm::X86::Tree::expand($tree, $offset)

Expand the node at the specified offset in the specified tree if it needs to be expanded and is not the root node (which cannot be expanded because it has no siblings to take substance from whereas as all other nodes do).  Set tree.found to the offset of the left sibling if the node at the specified offset was merged into it and freed else set tree.found to zero.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $offset    Offset of node block to expand

=head2 Nasm::X86::Tree::replace($tree, $point, $K, $D)

Replace the key/data/subTree at the specified point in the specified zmm with the values found in the tree key/data/sub tree fields.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $point     Point at which to extract
  3  $K         Keys zmm
  4  $D         Data zmm

B<Example:>


    my ($K, $D) = (31, 30);

    K(K => Rd(reverse 1..16))->loadZmm($K);
    K(K => Rd(reverse 1..16))->loadZmm($D);
    PrintOutStringNL "Start";
    PrintOutRegisterInHex $K, $D;

    my $a = CreateArea;
    my $t = $a->CreateTree;

    K(loop => 14)->for(sub
     {my ($index, $start, $next, $end) = @_;

      $t->key    ->copy($index);
      $t->data   ->copy($index * 2);
      $t->subTree->copy($index % 2);

      $t->replace(K(one=>1)<<$index, $K, $D);

      $index->outNL;
      PrintOutRegisterInHex $K, $D;
     });

    ok Assemble eq => <<END, avx512=>1;
  Start
   zmm31: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ..10
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ..10
  index: .... .... .... ...0
   zmm31: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ....
  index: .... .... .... ...1
   zmm31: .... ...1 ...2 ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...2 .... ....
  index: .... .... .... ...2
   zmm31: .... ...1 ...2 ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...4  .... ...2 .... ....
  index: .... .... .... ...3
   zmm31: .... ...1 ...A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...4
   zmm31: .... ...1 ...A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...5
   zmm31: .... ...1 ..2A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...6
   zmm31: .... ...1 ..2A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...7
   zmm31: .... ...1 ..AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...8
   zmm31: .... ...1 ..AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...9
   zmm31: .... ...1 .2AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...A
   zmm31: .... ...1 .2AA ...2  .... ...3 .... ...4 - .... ...5 .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...B
   zmm31: .... ...1 .AAA ...2  .... ...3 .... ...4 - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...C
   zmm31: .... ...1 .AAA ...2  .... ...3 .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ...3 .... ..18 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  index: .... .... .... ...D
   zmm31: .... ...1 2AAA ...2  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
   zmm30: .... ...1 .... ...2  .... ..1A .... ..18 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
  END


=head2 Nasm::X86::Tree::overWriteKeyDataTreeInLeaf($tree, $point, $K, $D, $IK, $ID, $subTree)

Over write an existing key/data/sub tree triple in a set of zmm registers and set the tree bit as indicated.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $point     Register point at which to overwrite formatted as a one in a sea of zeros
  3  $K         Key
  4  $D         Data
  5  $IK        Insert key
  6  $ID        Insert data
  7  $subTree   Sub tree if tree.

=head2 Nasm::X86::Tree::indexXX($tree, $key, $K, $cmp, $inc, %options)

Return, as a variable, the mask obtained by performing a specified comparison on the key area of a node against a specified key.

     Parameter  Description
  1  $tree      Tree definition
  2  $key       Key to search for as a variable or a zmm containing a copy of the key to be searched for in each slot
  3  $K         Zmm containing keys
  4  $cmp       Comparison from B<Vpcmp>
  5  $inc       Whether to increment the result by one
  6  %options   Options

=head2 Nasm::X86::Tree::indexEq($tree, $key, $K, %options)

Return the  position of a key in a zmm equal to the specified key as a point in a variable.

     Parameter  Description
  1  $tree      Tree definition
  2  $key       Key as a variable
  3  $K         Zmm containing keys
  4  %options   Options

B<Example:>


    my $tree = DescribeTree;

    my $K = 31;

    K(K => Rd(0..15))->loadZmm($K);
    $tree->lengthIntoKeys($K, K length => 13);

    K(loop => 16)->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $f = $tree->indexEq ($index, $K);
      $index->outRightInDec( 2);
      $f    ->outRightInBin(14);
      PrintOutStringNL " |"
     });

    ok Assemble eq => <<END, avx512=>1;
   0             1 |
   1            10 |
   2           100 |
   3          1000 |
   4         10000 |
   5        100000 |
   6       1000000 |
   7      10000000 |
   8     100000000 |
   9    1000000000 |
  10   10000000000 |
  11  100000000000 |
  12 1000000000000 |
  13               |
  14               |
  15               |
  END

    my $tree = DescribeTree();
    $tree->maskForFullKeyArea(7);                                                 # Mask for full key area
    PrintOutRegisterInHex k7;
    $tree->maskForFullNodesArea(7);                                               # Mask for full nodes area
    PrintOutRegisterInHex k7;
    ok Assemble eq => <<END, avx512=>1;
      k7: .... .... .... 3FFF
      k7: .... .... .... 7FFF
  END


=head2 Nasm::X86::Tree::insertionPoint($tree, $key, $K, %options)

Return the position at which a key should be inserted into a zmm as a point in a variable.

     Parameter  Description
  1  $tree      Tree definition
  2  $key       Key as a variable
  3  $K         Zmm containing keys
  4  %options   Options

B<Example:>


    my $tree = DescribeTree(length => 7);

    my $K = 31;

    K(K => Rd(map {2*$_} 1..16))->loadZmm($K);
    $tree->lengthIntoKeys($K, K length => 13);

    K(loop => 32)->for(sub
     {my ($index, $start, $next, $end) = @_;
      my $f = $tree->insertionPoint($index, $K);
      $index->outRightInDec( 2);
      $f    ->outRightInBin(16);
      PrintOutStringNL " |"
     });

    ok Assemble eq => <<END, avx512=>1;
   0               1 |
   1               1 |
   2              10 |
   3              10 |
   4             100 |
   5             100 |
   6            1000 |
   7            1000 |
   8           10000 |
   9           10000 |
  10          100000 |
  11          100000 |
  12         1000000 |
  13         1000000 |
  14        10000000 |
  15        10000000 |
  16       100000000 |
  17       100000000 |
  18      1000000000 |
  19      1000000000 |
  20     10000000000 |
  21     10000000000 |
  22    100000000000 |
  23    100000000000 |
  24   1000000000000 |
  25   1000000000000 |
  26  10000000000000 |
  27  10000000000000 |
  28  10000000000000 |
  29  10000000000000 |
  30  10000000000000 |
  31  10000000000000 |
  END


=head2 Nasm::X86::Tree::indexEqLt($tree, $key, $K, $setEq, $setLt)

Set the specified registers with the equals point and the insertion point for the specified key in the specified zmm.

     Parameter  Description
  1  $tree      Tree definition
  2  $key       Zmm containing a copy of the key to be searched for in each slot
  3  $K         Zmm to check
  4  $setEq     Bound register to set with equals point
  5  $setLt     Bound register to set with insertion point.

B<Example:>


    my $tree = DescribeTree;

    my $K = 31, my $key = 30;

    K(K => Rd(0..15))->loadZmm($K);
    $tree->lengthIntoKeys($K, K length => 13);

    K(loop => 16)->for(sub
     {my ($index, $start, $next, $end) = @_;
      $index->setReg(rdi);
      Vpbroadcastd zmm($key), edi;

      $tree->indexEqLt ($key, $K, r15, r14);
      Pushfq;
      my $f = V key => r15;
      $index->outRightInDec( 2);
      $f    ->outRightInBin(14);
      PrintOutString   " ";
      Popfq;
      PrintOutZF;
     });

    ok Assemble eq => <<END, avx512=>1;
   0             1 ZF=0
   1            10 ZF=0
   2           100 ZF=0
   3          1000 ZF=0
   4         10000 ZF=0
   5        100000 ZF=0
   6       1000000 ZF=0
   7      10000000 ZF=0
   8     100000000 ZF=0
   9    1000000000 ZF=0
  10   10000000000 ZF=0
  11  100000000000 ZF=0
  12 1000000000000 ZF=0
  13               ZF=1
  14               ZF=1
  15               ZF=1
  END


=head2 Nasm::X86::Tree::insertKeyDataTreeIntoLeaf($tree, $point, $F, $K, $D, $IK, $ID, $subTree)

Insert a new key/data/sub tree triple into a set of zmm registers if there is room, increment the length of the node and set the tree bit as indicated and increment the number of elements in the tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $point     Register point at which to insert formatted as a one in a sea of zeros
  3  $F         First
  4  $K         Key
  5  $D         Data
  6  $IK        Insert key
  7  $ID        Insert data
  8  $subTree   Sub tree if tree.

=head2 Nasm::X86::Tree::splitNode($tree, $offset)

Split a node if it it is full returning a variable that indicates whether a split occurred or not.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $offset    Offset of block in area of tree as a variable

=head2 Nasm::X86::Tree::splitNotRoot($tree, $newRight, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN)

Split a non root left node pushing its excess right and up.

      Parameter  Description
   1  $tree      Tree definition
   2  $newRight  Variable offset in area of right node block
   3  $PK        Parent keys zmm
   4  $PD        Data zmm
   5  $PN        Nodes zmm
   6  $LK        Left keys zmm
   7  $LD        Data zmm
   8  $LN        Nodes zmm
   9  $RK        Right keys
  10  $RD        Data
  11  $RN        Node zmm

=head2 Nasm::X86::Tree::splitRoot($tree, $nLeft, $nRight, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN)

Split a non root node into left and right nodes with the left half left in the left node and splitting key/data pushed into the parent node with the remainder pushed into the new right node.

      Parameter  Description
   1  $tree      Tree definition
   2  $nLeft     Variable offset in area of new left node block
   3  $nRight    Variable offset in area of new right node block
   4  $PK        Parent keys zmm
   5  $PD        Data zmm
   6  $PN        Nodes zmm
   7  $LK        Left keys zmm
   8  $LD        Data zmm
   9  $LN        Nodes zmm
  10  $RK        Right keys
  11  $RD        Data
  12  $RN        Nodes zmm

=head2 Nasm::X86::Tree::zero($tree)

Zero the return fields of a tree descriptor.

     Parameter  Description
  1  $tree      Tree descriptor

=head2 Nasm::X86::Tree::findAndReload($t, $key)

Find a key in the specified tree and clone it is it is a sub tree.

     Parameter  Description
  1  $t         Tree descriptor
  2  $key       Key as a dword

=head2 Nasm::X86::Tree::leftOrRightMost($tree, $dir, $node, $offset)

Return the offset of the left most or right most node.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $dir       Direction: left = 0 or right = 1
  3  $node      Start node
  4  $offset    Offset of located node

=head2 Nasm::X86::Tree::leftMost($t, $node, $offset)

Return the offset of the left most node from the specified node.

     Parameter  Description
  1  $t         Tree descriptor
  2  $node      Start node
  3  $offset    Returned offset

=head2 Nasm::X86::Tree::rightMost($t, $node, $offset)

Return the offset of the left most node from the specified node.

     Parameter  Description
  1  $t         Tree descriptor
  2  $node      Start node
  3  $offset    Returned offset

=head2 Nasm::X86::Tree::depth($tree, $node)

Return the depth of a node within a tree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $node      Node

=head2 Nasm::X86::Tree::isTree($t, $zmm, $point)

Set the Zero Flag to oppose the tree bit in the numbered zmm register holding the keys of a node to indicate whether the data element indicated by the specified register is an offset to a sub tree in the containing area or not.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm register holding the keys for a node in the tree
  3  $point     Register showing point to test

B<Example:>



    my $t = DescribeTree;
    Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                       $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

    $t->getTreeBits(31, r8);
    V(TreeBits => r8)->outRightInHexNL(4);
    PrintOutRegisterInHex 31;

    Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
               101
              1001
             10011
    13
   zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  ZF=0
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
   zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::getTreeBit($t, $zmm, $point, %options)

Get the tree bit from the numbered zmm at the specified point and return it in a variable as a one or a zero.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Register showing point to test
  3  $point     Numbered zmm register holding the keys for a node in the tree
  4  %options   Options

=head2 Nasm::X86::Tree::setOrClearTreeBit($t, $set, $point, $zmm)

Set or clear the tree bit selected by the specified point in the numbered zmm register holding the keys of a node to indicate that the data element indicated by the specified register is an offset to a sub tree in the containing area.

     Parameter  Description
  1  $t         Tree descriptor
  2  $set       Set if true else clear
  3  $point     Register holding point to set
  4  $zmm       Numbered zmm register holding the keys for a node in the tree

=head2 Nasm::X86::Tree::setTreeBit($t, $zmm, $point)

Set the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm register holding the keys for a node in the tree
  3  $point     Register holding the point to clear

=head2 Nasm::X86::Tree::clearTreeBit($t, $zmm, $point)

Clear the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm register holding the keys for a node in the tree
  3  $point     Register holding register holding the point to set

=head2 Nasm::X86::Tree::setOrClearTreeBitToMatchContent($t, $zmm, $point, $content)

Set or clear the tree bit pointed to by the specified register depending on the content of the specified variable.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered keys zmm
  3  $point     Register indicating point
  4  $content   Content indicating zero or one

=head2 Nasm::X86::Tree::getTreeBits($t, $zmm, $register)

Load the tree bits from the numbered zmm into the specified register.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  $register  Target register

B<Example:>



    my $t = DescribeTree;
    Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                       $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

    $t->getTreeBits(31, r8);
    V(TreeBits => r8)->outRightInHexNL(4);
    PrintOutRegisterInHex 31;

    Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
               101
              1001
             10011
    13
   zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  ZF=0
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
   zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::setTreeBits($t, $zmm, $register)

Put the tree bits in the specified register into the numbered zmm.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  $register  Target register

B<Example:>



    my $t = DescribeTree;
    Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                       $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

    $t->getTreeBits(31, r8);
    V(TreeBits => r8)->outRightInHexNL(4);
    PrintOutRegisterInHex 31;

    Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
               101
              1001
             10011
    13
   zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  ZF=0
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
   zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::insertTreeBit($t, $onz, $zmm, $point)

Insert a zero or one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

     Parameter  Description
  1  $t         Tree descriptor
  2  $onz       0 - zero or 1 - one
  3  $zmm       Numbered zmm
  4  $point     Register indicating point

=head2 Nasm::X86::Tree::insertZeroIntoTreeBits($t, $zmm, $point)

Insert a zero into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  $point     Register indicating point

B<Example:>



    my $t = DescribeTree;
    Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                       $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

    $t->getTreeBits(31, r8);
    V(TreeBits => r8)->outRightInHexNL(4);
    PrintOutRegisterInHex 31;

    Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
               101
              1001
             10011
    13
   zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  ZF=0
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
   zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::insertOneIntoTreeBits($t, $zmm, $point)

Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  $point     Register indicating point

B<Example:>



    my $t = DescribeTree;
    Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
    Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                       $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
    Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

    $t->getTreeBits(31, r8);
    V(TreeBits => r8)->outRightInHexNL(4);
    PrintOutRegisterInHex 31;

    Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
    Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

    Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

    ok Assemble eq => <<END, avx512=>1;
   zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
   zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
               101
              1001
             10011
    13
   zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  ZF=0
  ZF=0
  ZF=1
  ZF=1
  ZF=0
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
  ZF=1
   zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::insertIntoTreeBits($t, $zmm, $point, $content)

Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

     Parameter  Description
  1  $t         Tree descriptor
  2  $zmm       Numbered zmm
  3  $point     Register indicating point
  4  $content   Bit to insert

=head2 Nasm::X86::Tree::extract($tree, $point, $K, $D, $N)

Extract the key/data/node and tree bit at the specified point from the block held in the specified zmm registers.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $point     Point at which to extract
  3  $K         Keys zmm
  4  $D         Data zmm
  5  $N         Node zmm

B<Example:>


    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))->loadZmm($K);
    K(K => Rd( 1..16))->loadZmm($D);
    K(K => Rd(map {0} 1..16))->loadZmm($N);

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my $p = K(one => 1) << K three => 3;
    Mov r15, 0xAAAA;
    $t->setTreeBits($K, r15);

    PrintOutStringNL "Start";
    PrintOutRegisterInHex 31, 30, 29;

    $t->extract($p, $K, $D, $N);

    PrintOutStringNL "Finish";
    PrintOutRegisterInHex 31, 30, 29;

    ok Assemble eq => <<END, avx512=>1;
  Start
   zmm31: .... ..10 2AAA ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  Finish
   zmm31: .... ..10 2AAA ...E  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  END


=head2 Nasm::X86::Tree::extractFirst($tree, $K, $D, $N)

Extract the first key/data and tree bit at the specified point from the block held in the specified zmm registers and place the extracted data/bit in tree data/subTree.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $K         Keys zmm
  3  $D         Data zmm
  4  $N         Node zmm

B<Example:>


    my ($K, $D, $N) = (31, 30, 29);

    K(K => Rd( 1..16))       ->loadZmm($K);
    K(K => Rd( 1..16))       ->loadZmm($D);
    K(K => Rd(map {0} 1..16))->loadZmm($N);

    my $a = CreateArea;
    my $t = $a->CreateTree;

    my $p = K(one => 1) << K three => 3;
    Mov r15, 0xAAAA;
    $t->setTreeBits($K, r15);

    PrintOutStringNL "Start";
    PrintOutRegisterInHex 31, 30, 29;

    K(n=>4)->for(sub
     {my ($index, $start, $next, $end) = @_;

      $t->extractFirst($K, $D, $N);

      PrintOutStringNL "-------------";
      $index->outNL;
      PrintOutRegisterInHex 31, 30, 29;

      $t->data->outNL;
      $t->subTree->outNL;
      $t->lengthFromKeys($K)->outNL;
     });

    ok Assemble eq => <<END, avx512=>1;
  Start
   zmm31: .... ..10 2AAA ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  -------------
  index: .... .... .... ...0
   zmm31: .... ..10 1555 ...E  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...4  .... ...3 .... ...2
   zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...4  .... ...3 .... ...2
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  data   : .... .... .... ...1
  subTree: .... .... .... ...0
  b at offset 56 in zmm31: .... .... .... ...E
  -------------
  index: .... .... .... ...1
   zmm31: .... ..10 .AAA ...D  .... ...E .... ...E - .... ...E .... ...D  .... ...C .... ...B + .... ...A .... ...9  .... ...8 .... ...7 - .... ...6 .... ...5  .... ...4 .... ...3
   zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...D  .... ...C .... ...B + .... ...A .... ...9  .... ...8 .... ...7 - .... ...6 .... ...5  .... ...4 .... ...3
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  data   : .... .... .... ...2
  subTree: .... .... .... ...1
  b at offset 56 in zmm31: .... .... .... ...D
  -------------
  index: .... .... .... ...2
   zmm31: .... ..10 .555 ...C  .... ...E .... ...E - .... ...E .... ...E  .... ...D .... ...C + .... ...B .... ...A  .... ...9 .... ...8 - .... ...7 .... ...6  .... ...5 .... ...4
   zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...E  .... ...D .... ...C + .... ...B .... ...A  .... ...9 .... ...8 - .... ...7 .... ...6  .... ...5 .... ...4
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  data   : .... .... .... ...3
  subTree: .... .... .... ...0
  b at offset 56 in zmm31: .... .... .... ...C
  -------------
  index: .... .... .... ...3
   zmm31: .... ..10 .2AA ...B  .... ...E .... ...E - .... ...E .... ...E  .... ...E .... ...D + .... ...C .... ...B  .... ...A .... ...9 - .... ...8 .... ...7  .... ...6 .... ...5
   zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...E  .... ...E .... ...D + .... ...C .... ...B  .... ...A .... ...9 - .... ...8 .... ...7  .... ...6 .... ...5
   zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
  data   : .... .... .... ...4
  subTree: .... .... .... ...1
  b at offset 56 in zmm31: .... .... .... ...B
  END


=head2 Nasm::X86::Tree::mergeOrSteal($tree, $offset)

Merge the block at the specified offset with its right sibling or steal from it. If there is no  right sibling then do the same thing but with the left sibling.  The supplied block must not be the root. The key we are looking for must be in the tree key field.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $offset    Offset of non root block that might need to merge or steal

=head2 Nasm::X86::Tree::stealFromRight($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN)

Steal one key from the node on the right where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else zero.

      Parameter  Description
   1  $tree      Tree definition
   2  $PK        Parent keys zmm
   3  $PD        Data zmm
   4  $PN        Nodes zmm
   5  $LK        Left keys zmm
   6  $LD        Data zmm
   7  $LN        Nodes zmm
   8  $RK        Right keys
   9  $RD        Data
  10  $RN        Nodes zmm.

=head2 Nasm::X86::Tree::stealFromLeft($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN)

Steal one key from the node on the left where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else  zero.

      Parameter  Description
   1  $tree      Tree definition
   2  $PK        Parent keys zmm
   3  $PD        Data zmm
   4  $PN        Nodes zmm
   5  $LK        Left keys zmm
   6  $LD        Data zmm
   7  $LN        Nodes zmm
   8  $RK        Right keys
   9  $RD        Data
  10  $RN        Nodes zmm.

=head2 Nasm::X86::Tree::merge($tree, $PK, $PD, $PN, $LK, $LD, $LN, $RK, $RD, $RN)

Merge a left and right node if they are at minimum size.

      Parameter  Description
   1  $tree      Tree definition
   2  $PK        Parent keys zmm
   3  $PD        Data zmm
   4  $PN        Nodes zmm
   5  $LK        Left keys zmm
   6  $LD        Data zmm
   7  $LN        Nodes zmm
   8  $RK        Right keys
   9  $RD        Data
  10  $RN        Nodes zmm.

=head2 Nasm::X86::Tree::deleteFirstKeyAndData($tree, $K, $D)

Delete the first element of a leaf mode returning its characteristics in the calling tree descriptor.

     Parameter  Description
  1  $tree      Tree definition
  2  $K         Keys zmm
  3  $D         Data zmm

=head2 Nasm::X86::Tree::push($tree, $data)

Push a data value onto a tree. If the data is a reference to a tree then the offset of the first block of the tree is pushed.

     Parameter  Description
  1  $tree      Tree descriptor
  2  $data      Variable data

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $N = K loop => 16;
    $N->for(sub
     {my ($i) = @_;
      $t->push($i);
     });

    $t->peek(1)->data ->outNL;
    $t->peek(2)->data ->outNL;
    $t->peek(3)->found->outNL;
    $t->peek(2 * $N    )->found->outNL;

    $t->size->outNL;
    $t->get(8); $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;

    $N->for(sub
     {my ($i) = @_;
      $t->pop; $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;
     });
  # $t->pop; $t->found->outNL("f: ");

    ok Assemble eq => <<END, clocks => 29_852;
  data   : .... .... .... ...F
  data   : .... .... .... ...E
  found  : .... .... .... ..40
  found  : .... .... .... ...0
  size of tree: .... .... .... ..10
  f: .... .... .... ...2 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...F data   : .... .... .... ...F
  f: .... .... .... ...1 i: .... .... .... ...E data   : .... .... .... ...E
  f: .... .... .... ...1 i: .... .... .... ...D data   : .... .... .... ...D
  f: .... .... .... ...1 i: .... .... .... ...C data   : .... .... .... ...C
  f: .... .... .... ...1 i: .... .... .... ...B data   : .... .... .... ...B
  f: .... .... .... ...1 i: .... .... .... ...A data   : .... .... .... ...A
  f: .... .... .... ...1 i: .... .... .... ...9 data   : .... .... .... ...9
  f: .... .... .... ...1 i: .... .... .... ...8 data   : .... .... .... ...8
  f: .... .... .... ...1 i: .... .... .... ...7 data   : .... .... .... ...7
  f: .... .... .... ...1 i: .... .... .... ...6 data   : .... .... .... ...6
  f: .... .... .... ...1 i: .... .... .... ...5 data   : .... .... .... ...5
  f: .... .... .... ...1 i: .... .... .... ...4 data   : .... .... .... ...4
  f: .... .... .... ...1 i: .... .... .... ...3 data   : .... .... .... ...3
  f: .... .... .... ...1 i: .... .... .... ...2 data   : .... .... .... ...2
  f: .... .... .... ...1 i: .... .... .... ...1 data   : .... .... .... ...1
  f: .... .... .... ...1 i: .... .... .... ...0 data   : .... .... .... ...0
  END

    my $b = Rb(0x41..0x51);
    my $a = CreateArea;
    my $T;
    for my $i(1..8)
     {my $t = $a->CreateTree;
      $t->appendAscii(K(address=> $b), K(size => 1));
      $t->push($T) if $T;
      $T = $t;
     }

    $T->dump8xx("T");
    ok Assemble eq => <<END, trace=>0;
  T
  Tree: .... .... .... .740
  At:      780                                                                                length:        2,  data:      7C0,  nodes:      800,  first:      740, root, leaf,  trees:            10
    Index:        0        1
    Keys :        0        1
    Data :       41      64*
     Tree:      640
       At:      680                                                                           length:        2,  data:      6C0,  nodes:      700,  first:      640, root, leaf,  trees:            10
         Index:        0        1
         Keys :        0        1
         Data :       41      54*
          Tree:      540
            At:      580                                                                      length:        2,  data:      5C0,  nodes:      600,  first:      540, root, leaf,  trees:            10
              Index:        0        1
              Keys :        0        1
              Data :       41      44*
               Tree:      440
                 At:      480                                                                 length:        2,  data:      4C0,  nodes:      500,  first:      440, root, leaf,  trees:            10
                   Index:        0        1
                   Keys :        0        1
                   Data :       41      34*
                    Tree:      340
                      At:      380                                                            length:        2,  data:      3C0,  nodes:      400,  first:      340, root, leaf,  trees:            10
                        Index:        0        1
                        Keys :        0        1
                        Data :       41      24*
                         Tree:      240
                           At:      280                                                       length:        2,  data:      2C0,  nodes:      300,  first:      240, root, leaf,  trees:            10
                             Index:        0        1
                             Keys :        0        1
                             Data :       41      14*
                              Tree:      140
                                At:      180                                                  length:        2,  data:      1C0,  nodes:      200,  first:      140, root, leaf,  trees:            10
                                  Index:        0        1
                                  Keys :        0        1
                                  Data :       41       4*
                                   Tree:       40
                                     At:       80                                             length:        1,  data:       C0,  nodes:      100,  first:       40, root, leaf
                                       Index:        0
                                       Keys :        0
                                       Data :       41
                                     end
                                end
                           end
                      end
                 end
            end
       end
  end
  END


=head2 Nasm::X86::Tree::dumpWithWidth($tree, $title, $width, $margin, $first, $keyX, $dataX)

Dump a tree and all its sub trees.

     Parameter  Description
  1  $tree      Tree
  2  $title     Title
  3  $width     Width of offset field
  4  $margin    The maximum width of the indented area
  5  $first     Whether to print the offset of the tree
  6  $keyX      Whether to print the key field in hex or decimal
  7  $dataX     Whether to print the data field in hex or decimal

=head2 Nasm::X86::Tree::dump($tree, $title)

Dump a tree and all its sub trees.

     Parameter  Description
  1  $tree      Tree
  2  $title     Title

=head2 Nasm::X86::Tree::dump8($tree, $title)

Dump a tree and all its sub trees using 8 character fields for numbers.

     Parameter  Description
  1  $tree      Tree
  2  $title     Title

=head2 Nasm::X86::Tree::dump8xx($tree, $title)

Dump a tree and all its sub trees using 8 character fields for numbers printing the keys and data in hexadecimal.

     Parameter  Description
  1  $tree      Tree
  2  $title     Title

B<Example:>


    my $a = CreateArea;
    my $t = $a->CreateTree;
    my $T = $a->CreateTree;

    $T->push(K key => 1);
    $t->push($T);
    $t->dump8xx('AA');
    my $s = $t->popSubTree;
    $t->dump8xx('BB');
    $s->dump8xx('CC');
    ok Assemble eq => <<END, avx512=>1;
  AA
  Tree: .... .... .... ..40
  At:      180                                                                                length:        1,  data:      1C0,  nodes:      200,  first:       40, root, leaf,  trees:             1
    Index:        0
    Keys :        0
    Data :       8*
     Tree:       80
       At:       C0                                                                           length:        1,  data:      100,  nodes:      140,  first:       80, root, leaf
         Index:        0
         Keys :        0
         Data :        1
       end
  end
  BB
  - empty
  CC
  Tree: .... .... .... ..80
  At:       C0                                                                                length:        1,  data:      100,  nodes:      140,  first:       80, root, leaf
    Index:        0
    Keys :        0
    Data :        1
  end
  END


=head2 compactRangeIntoHex(@P)

Compact a range of numbers into hexadecimal.

     Parameter  Description
  1  @P         Numbers to compact

B<Example:>



  ok compactRangeIntoHex(qw(0x1 0x2 0x3 0x5 0x7 0x8 0x9 0xb)) eq                    # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲



=head2 Nasm::X86::Unisyn::Lex::Number::S {0}(sub Nasm::X86::Unisyn::Lex::Number::F {1})

Start symbol.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::Number::F {1}  P End symbol.

=head2 Nasm::X86::Unisyn::Lex::Number::F {1}()

End symbol.


=head2 Nasm::X86::Unisyn::Lex::Number::A {2}(sub Nasm::X86::Unisyn::Lex::Letter::A {(0x0..0x7f, 0x24b6..0x24e9)})

ASCII characters extended with circled characters to act as escape sequences.

     Parameter                                          Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::A {(0x0..0x7f  P
  2  0x24b6..0x24e9)}

=head2 Nasm::X86::Unisyn::Lex::Number::p {3}(sub Nasm::X86::Unisyn::Lex::Letter::p {(0x1d468...0x1d49b, 0x1d71c..0x1d755, map {ord} qw(₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₀))})

Prefix operator - applies only to the following variable or bracketed term.

     Parameter                                                  Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::p {(0x1d468...0x1d49b  P
  2  0x1d71c..0x1d755
  3  map {ord} qw(₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₀))}

=head2 Nasm::X86::Unisyn::Lex::Number::v {4}(sub Nasm::X86::Unisyn::Lex::Letter::v {(0x1d5d4...0x1d607, 0x1d756..0x1d78f)})

Variable names.

     Parameter                                                  Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::v {(0x1d5d4...0x1d607  P
  2  0x1d756..0x1d78f)}

=head2 Nasm::X86::Unisyn::Lex::Number::q {5}()

Suffix operator - applies only to the preceding variable or bracketed term.


=head2 Nasm::X86::Unisyn::Lex::Number::s {6}()

Infix operator with left to right binding at priority 1.


=head2 Nasm::X86::Unisyn::Lex::Number::b {7}(sub Nasm::X86::Unisyn::Lex::Letter::b)

Open.

     Parameter                              Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::b  P

=head2 Nasm::X86::Unisyn::Lex::Number::B {8}(sub Nasm::X86::Unisyn::Lex::Letter::B)

Close.

     Parameter                              Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::B  P

=head2 Nasm::X86::Unisyn::Lex::Letter::d()

Dyad 2 - Double struck


=head2 Nasm::X86::Unisyn::Lex::Letter::e()

Dyad 3 - Mono


=head2 Nasm::X86::Unisyn::Lex::Number::a {11}(sub Nasm::X86::Unisyn::Lex::Letter::a)

Assign infix operator with right to left binding at priority 4.

     Parameter                              Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::a  P

=head2 Nasm::X86::Unisyn::Lex::Letter::f()

Dyad 5 - Sans-serif Normal


=head2 Nasm::X86::Unisyn::Lex::Letter::g()

Dyad 6 - Sans-serif Bold


=head2 Nasm::X86::Unisyn::Lex::Letter::h()

Dyad 7  - Calligraphy - normal


=head2 Nasm::X86::Unisyn::Lex::Letter::i()

Dyad 8 - Calligraphy - bold


=head2 Nasm::X86::Unisyn::Lex::Letter::j()

Dyad 9 - Fraktur - Normal


=head2 Nasm::X86::Unisyn::Lex::Letter::k()

Dyad 10 - Fraktur - bold


=head2 Nasm::X86::Unisyn::Lex::Number::l {18}()

Dyad 11


=head2 Nasm::X86::Unisyn::Lex::Number::m {19}(sub Nasm::X86::Unisyn::Lex::Letter::m)

Dyad 12

     Parameter                              Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::m  P

=head2 Nasm::X86::Unisyn::Lex::Number::w {20}(sub Nasm::X86::Unisyn::Lex::Letter::w)

White space

     Parameter                              Description
  1  sub Nasm::X86::Unisyn::Lex::Letter::w  P

=head2 Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits {5}()

The number of bits needed to express a transition


=head2 Nasm::X86::Unisyn::Lex::Reason::Success           {0};(sub Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};)

Successful parse.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};  P Bad utf8 character encountered.

=head2 Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};(sub Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};)

Bad utf8 character encountered.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};  P Character not part of Earl Zero.

=head2 Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};(sub Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};)

Character not part of Earl Zero.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};  P Transition from one lexical item to another not allowed.

=head2 Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};(sub Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};)

Transition from one lexical item to another not allowed.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};  P Trailing closing bracket discovered.

=head2 Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};(sub Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};)

Trailing closing bracket discovered.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};  P Mismatched bracket.

=head2 Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};(sub Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};)

Mismatched bracket.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};  P Expected something after final character.

=head2 Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};(sub Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};)

Expected something after final character.

     Parameter                                                   Description
  1  sub Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};  P Open brackets not closed at end of.

=head2 Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};()

Open brackets not closed at end of.


=head2 Nasm::X86::Unisyn::Lex::position {0};(sub Nasm::X86::Unisyn::Lex::length   {1};)

Position of the parsed item in the input text.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::length   {1};  P Length of the lexical item in bytes.

=head2 Nasm::X86::Unisyn::Lex::length   {1};(sub Nasm::X86::Unisyn::Lex::type     {2};)

Length of the lexical item in bytes.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::type     {2};  P Type of the lexical item.

=head2 Nasm::X86::Unisyn::Lex::type     {2};(sub Nasm::X86::Unisyn::Lex::left     {3};)

Type of the lexical item.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::left     {3};  P Left operand.

=head2 Nasm::X86::Unisyn::Lex::left     {3};(sub Nasm::X86::Unisyn::Lex::right    {4};)

Left operand.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::right    {4};  P Right operand.

=head2 Nasm::X86::Unisyn::Lex::right    {4};(sub Nasm::X86::Unisyn::Lex::symbol   {5};)

Right operand.

     Parameter                                  Description
  1  sub Nasm::X86::Unisyn::Lex::symbol   {5};  P Symbol.

=head2 Nasm::X86::Unisyn::Lex::symbol   {5};()

Symbol.


=head2 Nasm::X86::Unisyn::DescribeParse()

Describe a parse - create a description on the stack to receive the results of a parse


=head2 sortHashKeysByIntegerValues($h)

Sort the keys of a hash whose values are integers by those values returning the keys so sorted as an array.

     Parameter  Description
  1  $h         Hash

=head2 Nasm::X86::Unisyn::Parse($pd)

Parse a string of utf8 characters.

     Parameter  Description
  1  $pd        Parse descriptor

=head2 Nasm::X86::Unisyn::Parse::dump($parse)

Dump a parse tree in order.

     Parameter  Description
  1  $parse     Parse

=head2 LocateIntelEmulator()

Locate the Intel Software Development Emulator.


=head2 getInstructionCount()

Get the number of instructions executed from the emulator mix file.


=head2 OptimizePopPush(%options)

Perform code optimizations.

     Parameter  Description
  1  %options   Options

=head2 OptimizeReload(%options)

Reload: a = b; b = a;  remove second - as redundant.

     Parameter  Description
  1  %options   Options

=head2 hasAvx512()

Check whether the current device has avx512 instructions or not.


=head2 lineNumbersToSubNamesFromSource()

Create a hash mapping line numbers to subroutine definitions.


=head2 locateRunTimeErrorInDebugTraceOutput($trace)

Locate the traceback of the last known good position in the trace file before the error occurred.

     Parameter  Description
  1  $trace     Trace mode

=head2 fixMixOutput()

Fix mix output so we know where the code comes from in the source file.


=head2 countComments($count)

Count the number of comments in the text of the program so we can see what code is being generated too often.

     Parameter  Description
  1  $count     Comment count

=head2 numberWithUnderScores($n)

Place underscores in the string representation of a number.

     Parameter  Description
  1  $n         Number to add commas to

=head2 onGitHub()

Whether we are on GitHub or not.


=head2 totalBytesAssembled()

Total size in bytes of all files assembled during testing.


=head2 testParseUnisyn($compose, $text, $parse)

Test the parse of a unisyn expression.

     Parameter  Description
  1  $compose   The composing expression used to create a unisyn expression
  2  $text      The expected composed expression
  3  $parse     The expected parse tree

=head2 BinarySearchD(%options)

Search for an ordered array of double words addressed by r15, of length held in r14 for a double word held in r13 and call the $then routine with the index in rax if found else call the $else routine.

     Parameter  Description
  1  %options   Options


=head1 Index


1 L<AllocateMemory|/AllocateMemory> - Allocate the variable specified amount of memory via mmap and return its address as a variable.

2 L<AndBlock|/AndBlock> - Short circuit B<and>: execute a block of code to test conditions which, if all of them pass, allows the first block to continue successfully else if one of the conditions fails we execute the optional fail block.

3 L<Assemble|/Assemble> - Assemble the generated code.

4 L<bFromX|/bFromX> - Get the byte from the numbered xmm register and return it in a variable.

5 L<bFromZ|/bFromZ> - Get the byte from the numbered zmm register and return it in a variable.

6 L<BinarySearchD|/BinarySearchD> - Search for an ordered array of double words addressed by r15, of length held in r14 for a double word held in r13 and call the $then routine with the index in rax if found else call the $else routine.

7 L<Block|/Block> - Execute a block of code with labels supplied for the start and end of this code.

8 L<bRegFromZmm|/bRegFromZmm> - Load the specified register from the byte at the specified offset located in the numbered zmm.

9 L<bRegIntoZmm|/bRegIntoZmm> - Put the byte content of the specified register into the byte in the numbered zmm at the specified offset in the zmm.

10 L<CallC|/CallC> - Call a C subroutine.

11 L<checkZmmRegister|/checkZmmRegister> - Check that a register is a zmm register.

12 L<ChooseRegisters|/ChooseRegisters> - Choose the specified numbers of registers excluding those on the specified list.

13 L<ClearMemory|/ClearMemory> - Clear memory with a variable address and variable length.

14 L<ClearRegisters|/ClearRegisters> - Clear registers by setting them to zero.

15 L<ClearZF|/ClearZF> - Clear the zero flag.

16 L<CloseFile|/CloseFile> - Close the file whose descriptor is in rax.

17 L<Comment|/Comment> - Insert a comment into the assembly code.

18 L<compactRangeIntoHex|/compactRangeIntoHex> - Compact a range of numbers into hexadecimal.

19 L<constantString|/constantString> - Return the address and length of a constant string as two variables.

20 L<convert_rax_from_utf32_to_utf8|/convert_rax_from_utf32_to_utf8> - Convert a utf32 character held in rax to a utf8 character held in rax.

21 L<ConvertUtf8ToUtf32|/ConvertUtf8ToUtf32> - Convert an allocated string of utf8 to an allocated string of utf32 and return its address and length.

22 L<CopyMemory|/CopyMemory> - Copy memory.

23 L<CopyMemory4K|/CopyMemory4K> - Copy memory in 4K byte blocks.

24 L<CopyMemory64|/CopyMemory64> - Copy memory in 64 byte blocks.

25 L<copyStructureMinusVariables|/copyStructureMinusVariables> - Copy a non recursive structure ignoring variables.

26 L<countComments|/countComments> - Count the number of comments in the text of the program so we can see what code is being generated too often.

27 L<CreateArea|/CreateArea> - Create an relocatable area and returns its address in rax.

28 L<createBitNumberFromAlternatingPattern|/createBitNumberFromAlternatingPattern> - Create a number from a bit pattern.

29 L<Cstrlen|/Cstrlen> - Length of the C style string addressed by rax returning the length in r15.

30 L<Db|/Db> - Layout bytes in the data segment and return their label.

31 L<Dbwdq|/Dbwdq> - Layout data.

32 L<Dd|/Dd> - Layout double words in the data segment and return their label.

33 L<DescribeArea|/DescribeArea> - Describe a relocatable area.

34 L<DescribeTree|/DescribeTree> - Return a descriptor for a tree with the specified options.

35 L<dFromPointInZ|/dFromPointInZ> - Get the double word from the numbered zmm register at a point specified by the variable or register and return it in a variable.

36 L<dFromX|/dFromX> - Get the double word from the numbered xmm register and return it in a variable.

37 L<dFromZ|/dFromZ> - Get the double word from the numbered zmm register and return it in a variable.

38 L<Dq|/Dq> - Layout quad words in the data segment and return their label.

39 L<dRegFromZmm|/dRegFromZmm> - Load the specified register from the double word at the specified offset located in the numbered zmm.

40 L<dRegIntoZmm|/dRegIntoZmm> - Put the specified register into the double word in the numbered zmm at the specified offset in the zmm.

41 L<Ds|/Ds> - Layout bytes in memory and return their label.

42 L<Dw|/Dw> - Layout words in the data segment and return their label.

43 L<Ef|/Ef> - Else if block for an If statement.

44 L<Else|/Else> - Else block for an If statement.

45 L<executeFileViaBash|/executeFileViaBash> - Execute the file named in a variable.

46 L<Exit|/Exit> - Exit with the specified return code or zero if no return code supplied.

47 L<Extern|/Extern> - Name external references.

48 L<extractRegisterNumberFromMM|/extractRegisterNumberFromMM> - Extract the register number from an *mm register.

49 L<Fail|/Fail> - Fail block for an L<AndBlock>.

50 L<fixMixOutput|/fixMixOutput> - Fix mix output so we know where the code comes from in the source file.

51 L<For|/For> - For - iterate the block as long as register is less than limit incrementing by increment each time.

52 L<ForEver|/ForEver> - Iterate for ever.

53 L<ForIn|/ForIn> - For - iterate the full block as long as register plus increment is less than than limit incrementing by increment each time then perform the last block with the remainder which might be of length zero.

54 L<Fork|/Fork> - Fork: create and execute a copy of the current process.

55 L<FreeMemory|/FreeMemory> - Free memory specified by variables.

56 L<getBwdqFromMm|/getBwdqFromMm> - Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable.

57 L<getInstructionCount|/getInstructionCount> - Get the number of instructions executed from the emulator mix file.

58 L<GetNextUtf8CharAsUtf32|/GetNextUtf8CharAsUtf32> - Get the next UTF-8 encoded character from the addressed memory and return it as a UTF-32 character as a variable along with the size of the input character and a variable indicating the success - 1 -  or failure  - 0 - of the operation.

59 L<GetPid|/GetPid> - Get process identifier.

60 L<GetPidInHex|/GetPidInHex> - Get process identifier in hex as 8 zero terminated bytes in rax.

61 L<GetPPid|/GetPPid> - Get parent process identifier.

62 L<GetUid|/GetUid> - Get userid of current process.

63 L<hasAvx512|/hasAvx512> - Check whether the current device has avx512 instructions or not.

64 L<Hash|/Hash> - Hash a string addressed by rax with length held in rdi and return the hash code in r15.

65 L<hexTranslateTable|/hexTranslateTable> - Create/address a hex translate table and return its label.

66 L<If|/If> - If statement.

67 L<ifAnd|/ifAnd> - Execute then or else block based on a multiplicity of AND conditions executed until one fails.

68 L<IfC|/IfC> - If the carry flag is set then execute the then block else the else block.

69 L<IfEq|/IfEq> - If equal execute the then block else the else block.

70 L<IfGe|/IfGe> - If greater than or equal execute the then block else the else block.

71 L<IfGt|/IfGt> - If greater than execute the then block else the else block.

72 L<IfLe|/IfLe> - If less than or equal execute the then block else the else block.

73 L<IfLt|/IfLt> - If less than execute the then block else the else block.

74 L<IfNc|/IfNc> - If the carry flag is not set then execute the then block else the else block.

75 L<IfNe|/IfNe> - If not equal execute the then block else the else block.

76 L<IfNs|/IfNs> - If signed less than execute the then block else the else block.

77 L<IfNz|/IfNz> - If the zero flag is not set then execute the then block else the else block.

78 L<ifOr|/ifOr> - Execute then or else block based on a multiplicity of OR conditions executed until one succeeds.

79 L<IfS|/IfS> - If signed greater than or equal execute the then block else the else block.

80 L<IfZ|/IfZ> - If the zero flag is set then execute the then block else the else block.

81 L<InsertOneIntoRegisterAtPoint|/InsertOneIntoRegisterAtPoint> - Insert a one into the specified register at the point indicated by another register.

82 L<InsertZeroIntoRegisterAtPoint|/InsertZeroIntoRegisterAtPoint> - Insert a zero into the specified register at the point indicated by another general purpose or mask register moving the higher bits one position to the left.

83 L<K|/K> - Define a constant variable.

84 L<Label|/Label> - Create a unique label.

85 L<lineNumbersToSubNamesFromSource|/lineNumbersToSubNamesFromSource> - Create a hash mapping line numbers to subroutine definitions.

86 L<Link|/Link> - Libraries to link with.

87 L<loadAreaIntoAssembly|/loadAreaIntoAssembly> - Load an area into the current assembly and return a descriptor for it.

88 L<LoadBitsIntoMaskRegister|/LoadBitsIntoMaskRegister> - Load a bit string specification into a mask register in two clocks.

89 L<LoadConstantIntoMaskRegister|/LoadConstantIntoMaskRegister> - Set a mask register equal to a constant.

90 L<LoadRegFromMm|/LoadRegFromMm> - Load the specified register from the numbered zmm at the quad offset specified as a constant number.

91 L<LoadZmm|/LoadZmm> - Load a numbered zmm with the specified bytes.

92 L<LocateIntelEmulator|/LocateIntelEmulator> - Locate the Intel Software Development Emulator.

93 L<locateRunTimeErrorInDebugTraceOutput|/locateRunTimeErrorInDebugTraceOutput> - Locate the traceback of the last known good position in the trace file before the error occurred.

94 L<Nasm::X86::Area::allocate|/Nasm::X86::Area::allocate> - Allocate the variable amount of space in the variable addressed area and return the offset of the allocation in the area as a variable.

95 L<Nasm::X86::Area::allocZmmBlock|/Nasm::X86::Area::allocZmmBlock> - Allocate a block to hold a zmm register in the specified area and return the offset of the block as a variable.

96 L<Nasm::X86::Area::allocZmmBlock3|/Nasm::X86::Area::allocZmmBlock3> - Allocate three zmm blocks in one go and return their offsets.

97 L<Nasm::X86::Area::append|/Nasm::X86::Area::append> - Append one area to another.

98 L<Nasm::X86::Area::appendMemory|/Nasm::X86::Area::appendMemory> - Append the variable addressed content in memory of variable size to the specified area  and return its offset in that area.

99 L<Nasm::X86::Area::appendVar|/Nasm::X86::Area::appendVar> - Append the contents of a variable to the specified area

100 L<Nasm::X86::Area::appendZmm|/Nasm::X86::Area::appendZmm> - Append the contents of the specified zmm to the specified area and returns its offset in that area as a variable,

101 L<Nasm::X86::Area::char|/Nasm::X86::Area::char> - Append a constant character expressed as a decimal number to the specified area.

102 L<Nasm::X86::Area::checkYggdrasilCreated|/Nasm::X86::Area::checkYggdrasilCreated> - Return a tree descriptor for the Yggdrasil world tree for an area.

103 L<Nasm::X86::Area::clear|/Nasm::X86::Area::clear> - Clear an area but keep it at the same size.

104 L<Nasm::X86::Area::clearZmmBlock|/Nasm::X86::Area::clearZmmBlock> - Clear the zmm block at the specified offset in the area.

105 L<Nasm::X86::Area::copyDescriptor|/Nasm::X86::Area::copyDescriptor> - Copy the description of one area into another

106 L<Nasm::X86::Area::CreateTree|/Nasm::X86::Area::CreateTree> - Create a tree in an area.

107 L<Nasm::X86::Area::DescribeTree|/Nasm::X86::Area::DescribeTree> - Return a descriptor for a tree in the specified area with the specified options.

108 L<Nasm::X86::Area::dump|/Nasm::X86::Area::dump> - Dump details of an area.

109 L<Nasm::X86::Area::free|/Nasm::X86::Area::free> - Free an area.

110 L<Nasm::X86::Area::freeChainSpace|/Nasm::X86::Area::freeChainSpace> - Count the number of blocks available on the free chain.

111 L<Nasm::X86::Area::freeZmmBlock|/Nasm::X86::Area::freeZmmBlock> - Free a block in an area by placing it on the free chain.

112 L<Nasm::X86::Area::getZmmBlock|/Nasm::X86::Area::getZmmBlock> - Get the block with the specified offset in the specified string and return it in the numbered zmm.

113 L<Nasm::X86::Area::makeReadOnly|/Nasm::X86::Area::makeReadOnly> - Make an area read only.

114 L<Nasm::X86::Area::makeWriteable|/Nasm::X86::Area::makeWriteable> - Make an area writable.

115 L<Nasm::X86::Area::nl|/Nasm::X86::Area::nl> - Append a new line to the area addressed by rax.

116 L<Nasm::X86::Area::out|/Nasm::X86::Area::out> - Print the specified area on sysout.

117 L<Nasm::X86::Area::outNL|/Nasm::X86::Area::outNL> - Print the specified area on sysout followed by a new line.

118 L<Nasm::X86::Area::peek|/Nasm::X86::Area::peek> - Peek at a variable on the stack

119 L<Nasm::X86::Area::peekZmm|/Nasm::X86::Area::peekZmm> - Peek at a zmm register from the stack in an area being used as a stack and return a variable containing its offset in the area so we can update the pushed zmm if desired.

120 L<Nasm::X86::Area::pop|/Nasm::X86::Area::pop> - Pop a variable from the stack in an area being used as a stack

121 L<Nasm::X86::Area::popZmm|/Nasm::X86::Area::popZmm> - Pop a zmm register from the stack in an area being used as a stack

122 L<Nasm::X86::Area::printOut|/Nasm::X86::Area::printOut> - Print part of the specified area on sysout.

123 L<Nasm::X86::Area::push|/Nasm::X86::Area::push> - Push the contents of a variable into an area

124 L<Nasm::X86::Area::pushZmm|/Nasm::X86::Area::pushZmm> - Push the contents of a zmm register into an area.

125 L<Nasm::X86::Area::putZmmBlock|/Nasm::X86::Area::putZmmBlock> - Write the numbered zmm to the block at the specified offset in the specified area.

126 L<Nasm::X86::Area::q|/Nasm::X86::Area::q> - Append a constant string to the area.

127 L<Nasm::X86::Area::ql|/Nasm::X86::Area::ql> - Append a constant quoted string containing new line characters to the specified area.

128 L<Nasm::X86::Area::read|/Nasm::X86::Area::read> - Read a file specified by a variable addressed zero terminated string and append its content to the specified area.

129 L<Nasm::X86::Area::readLibraryHeader|/Nasm::X86::Area::readLibraryHeader> - Create a tree mapping the numbers assigned to subroutine names to the offsets of the corresponding routines in a library returning the intersection so formed mapping the lexical item numbers (not names) encountered during parsing with the matching routines in the library.

130 L<Nasm::X86::Area::size|/Nasm::X86::Area::size> - Get the size of an area as a variable.

131 L<Nasm::X86::Area::stackSize|/Nasm::X86::Area::stackSize> - Size of the stack in an area being used as a stack

132 L<Nasm::X86::Area::stackVariable|/Nasm::X86::Area::stackVariable> - Peek at the variable indexed variable on the stack

133 L<Nasm::X86::Area::stackVariableSize|/Nasm::X86::Area::stackVariableSize> - Size of a stack of variables in an area.

134 L<Nasm::X86::Area::updateSpace|/Nasm::X86::Area::updateSpace> - Make sure that a variable addressed area has enough space to accommodate content of a variable size.

135 L<Nasm::X86::Area::used|/Nasm::X86::Area::used> - Return the currently used size of an area as a variable.

136 L<Nasm::X86::Area::write|/Nasm::X86::Area::write> - Write the content of the specified area to a file specified by a zero terminated string.

137 L<Nasm::X86::Area::writeLibraryHeader|/Nasm::X86::Area::writeLibraryHeader> - Load a hash of subroutine names and offsets into an area

138 L<Nasm::X86::Area::yggdrasil|/Nasm::X86::Area::yggdrasil> - Return a tree descriptor to the Yggdrasil world tree for an area creating the world tree Yggdrasil if it has not already been created.

139 L<Nasm::X86::Area::zero|/Nasm::X86::Area::zero> - Append a trailing zero to the area addressed by rax.

140 L<Nasm::X86::Subroutine::call|/Nasm::X86::Subroutine::call> - Call a sub optionally passing it parameters.

141 L<Nasm::X86::Subroutine::inline|/Nasm::X86::Subroutine::inline> - Call a sub by in-lining it, optionally passing it parameters.

142 L<Nasm::X86::Subroutine::mapStructureVariables|/Nasm::X86::Subroutine::mapStructureVariables> - Find the paths to variables in the copies of the structures passed as parameters and replace those variables with references so that in the subroutine we can refer to these variables regardless of where they are actually defined.

143 L<Nasm::X86::Subroutine::subroutineDefinition|/Nasm::X86::Subroutine::subroutineDefinition> - Get the definition of a subroutine from an area.

144 L<Nasm::X86::Subroutine::uploadStructureVariablesToNewStackFrame|/Nasm::X86::Subroutine::uploadStructureVariablesToNewStackFrame> - Create references to variables in parameter structures from variables in the stack frame of the subroutine.

145 L<Nasm::X86::Subroutine::uploadToNewStackFrame|/Nasm::X86::Subroutine::uploadToNewStackFrame> - Map a variable in the current stack into a reference in the next stack frame being the one that will be used by this sub.

146 L<Nasm::X86::Subroutine::validateParameters|/Nasm::X86::Subroutine::validateParameters> - Check that the parameters and structures presented in a call to a subroutine math those defined for the subroutine.

147 L<Nasm::X86::Subroutine::writeLibraryToArea|/Nasm::X86::Subroutine::writeLibraryToArea> - Write a subroutine library to an area then save the area in a file so that the subroutine can be reloaded at a later date either as separate file or via incorporation into a thing.

148 L<Nasm::X86::Tree::allocBlock|/Nasm::X86::Tree::allocBlock> - Allocate a keys/data/node block and place it in the numbered zmm registers.

149 L<Nasm::X86::Tree::append|/Nasm::X86::Tree::append> - Append the second source string to the first target string renumbering the keys of the source string to follow on from those of the target string.

150 L<Nasm::X86::Tree::appendAscii|/Nasm::X86::Tree::appendAscii> - Append ascii bytes in memory to a tree acting as a string.

151 L<Nasm::X86::Tree::by|/Nasm::X86::Tree::by> - Call the specified block with each element of the specified tree in ascending order.

152 L<Nasm::X86::Tree::clear|/Nasm::X86::Tree::clear> - Delete everything in the tree except the first block recording any memory liberated on the free chain.

153 L<Nasm::X86::Tree::clearTreeBit|/Nasm::X86::Tree::clearTreeBit> - Clear the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.

154 L<Nasm::X86::Tree::clone|/Nasm::X86::Tree::clone> - Clone a string.

155 L<Nasm::X86::Tree::cloneDescriptor|/Nasm::X86::Tree::cloneDescriptor> - Clone the descriptor of a tree to make a new tree descriptor.

156 L<Nasm::X86::Tree::copyDescriptor|/Nasm::X86::Tree::copyDescriptor> - Copy the description of one tree into another.

157 L<Nasm::X86::Tree::decLengthInKeys|/Nasm::X86::Tree::decLengthInKeys> - Decrement the number of keys in a keys block or complain if such is not possible.

158 L<Nasm::X86::Tree::decSize|/Nasm::X86::Tree::decSize> - Decrement the size of a tree.

159 L<Nasm::X86::Tree::decSizeInFirst|/Nasm::X86::Tree::decSizeInFirst> - Decrement the size field in the first block of a tree when the first block is held in a zmm register.

160 L<Nasm::X86::Tree::delete|/Nasm::X86::Tree::delete> - Find a key in a tree and delete it returning he value of the l=key deleted if found.

161 L<Nasm::X86::Tree::deleteFirstKeyAndData|/Nasm::X86::Tree::deleteFirstKeyAndData> - Delete the first element of a leaf mode returning its characteristics in the calling tree descriptor.

162 L<Nasm::X86::Tree::depth|/Nasm::X86::Tree::depth> - Return the depth of a node within a tree.

163 L<Nasm::X86::Tree::DescribeTree|/Nasm::X86::Tree::DescribeTree> - Create a description of a tree.

164 L<Nasm::X86::Tree::dump|/Nasm::X86::Tree::dump> - Dump a tree and all its sub trees.

165 L<Nasm::X86::Tree::dump8|/Nasm::X86::Tree::dump8> - Dump a tree and all its sub trees using 8 character fields for numbers.

166 L<Nasm::X86::Tree::dump8xx|/Nasm::X86::Tree::dump8xx> - Dump a tree and all its sub trees using 8 character fields for numbers printing the keys and data in hexadecimal.

167 L<Nasm::X86::Tree::dumpWithWidth|/Nasm::X86::Tree::dumpWithWidth> - Dump a tree and all its sub trees.

168 L<Nasm::X86::Tree::expand|/Nasm::X86::Tree::expand> - Expand the node at the specified offset in the specified tree if it needs to be expanded and is not the root node (which cannot be expanded because it has no siblings to take substance from whereas as all other nodes do).

169 L<Nasm::X86::Tree::extract|/Nasm::X86::Tree::extract> - Extract the key/data/node and tree bit at the specified point from the block held in the specified zmm registers.

170 L<Nasm::X86::Tree::extractFirst|/Nasm::X86::Tree::extractFirst> - Extract the first key/data and tree bit at the specified point from the block held in the specified zmm registers and place the extracted data/bit in tree data/subTree.

171 L<Nasm::X86::Tree::find|/Nasm::X86::Tree::find> - Find a key in a tree and tests whether the found data is a sub tree.

172 L<Nasm::X86::Tree::findAndReload|/Nasm::X86::Tree::findAndReload> - Find a key in the specified tree and clone it is it is a sub tree.

173 L<Nasm::X86::Tree::findFirst|/Nasm::X86::Tree::findFirst> - Find the first element in a tree and set B<found>|B<key>|B<data>|B<subTree> to show the result.

174 L<Nasm::X86::Tree::findLast|/Nasm::X86::Tree::findLast> - Find the last key in a tree - crucial for stack like operations.

175 L<Nasm::X86::Tree::findNext|/Nasm::X86::Tree::findNext> - Find the next key greater than the one specified.

176 L<Nasm::X86::Tree::findPrev|/Nasm::X86::Tree::findPrev> - Find the previous key less than the one specified.

177 L<Nasm::X86::Tree::findSubTree|/Nasm::X86::Tree::findSubTree> - Find a key in the specified tree and create a sub tree from the data field if possible.

178 L<Nasm::X86::Tree::firstFromMemory|/Nasm::X86::Tree::firstFromMemory> - Load the first block for a tree into the numbered zmm.

179 L<Nasm::X86::Tree::firstIntoMemory|/Nasm::X86::Tree::firstIntoMemory> - Save the first block of a tree in the numbered zmm back into memory.

180 L<Nasm::X86::Tree::firstNode|/Nasm::X86::Tree::firstNode> - Return as a variable the last node block in the specified tree node held in a zmm.

181 L<Nasm::X86::Tree::free|/Nasm::X86::Tree::free> - Free all the memory used by a tree.

182 L<Nasm::X86::Tree::freeBlock|/Nasm::X86::Tree::freeBlock> - Free a keys/data/node block whose keys  block entry is located at the specified offset.

183 L<Nasm::X86::Tree::get|/Nasm::X86::Tree::get> - Retrieves the element at the specified zero based index in the stack.

184 L<Nasm::X86::Tree::getBlock|/Nasm::X86::Tree::getBlock> - Get the keys, data and child nodes for a tree node from the specified offset in the area for the tree.

185 L<Nasm::X86::Tree::getKeyString|/Nasm::X86::Tree::getKeyString> - Find a string in a string tree and return the associated data and find status in the data and found fields of the tree.

186 L<Nasm::X86::Tree::getLoop|/Nasm::X86::Tree::getLoop> - Return the value of the loop field as a variable.

187 L<Nasm::X86::Tree::getTreeBit|/Nasm::X86::Tree::getTreeBit> - Get the tree bit from the numbered zmm at the specified point and return it in a variable as a one or a zero.

188 L<Nasm::X86::Tree::getTreeBits|/Nasm::X86::Tree::getTreeBits> - Load the tree bits from the numbered zmm into the specified register.

189 L<Nasm::X86::Tree::incLengthInKeys|/Nasm::X86::Tree::incLengthInKeys> - Increment the number of keys in a keys block or complain if such is not possible.

190 L<Nasm::X86::Tree::incSize|/Nasm::X86::Tree::incSize> - Increment the size of a tree.

191 L<Nasm::X86::Tree::incSizeInFirst|/Nasm::X86::Tree::incSizeInFirst> - Increment the size field in the first block of a tree when the first block is held in a zmm register.

192 L<Nasm::X86::Tree::indexEq|/Nasm::X86::Tree::indexEq> - Return the  position of a key in a zmm equal to the specified key as a point in a variable.

193 L<Nasm::X86::Tree::indexEqLt|/Nasm::X86::Tree::indexEqLt> - Set the specified registers with the equals point and the insertion point for the specified key in the specified zmm.

194 L<Nasm::X86::Tree::indexNode|/Nasm::X86::Tree::indexNode> - Return, as a variable, the point mask obtained by testing the nodes in a block for specified offset.

195 L<Nasm::X86::Tree::indexXX|/Nasm::X86::Tree::indexXX> - Return, as a variable, the mask obtained by performing a specified comparison on the key area of a node against a specified key.

196 L<Nasm::X86::Tree::insertIntoTreeBits|/Nasm::X86::Tree::insertIntoTreeBits> - Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

197 L<Nasm::X86::Tree::insertionPoint|/Nasm::X86::Tree::insertionPoint> - Return the position at which a key should be inserted into a zmm as a point in a variable.

198 L<Nasm::X86::Tree::insertKeyDataTreeIntoLeaf|/Nasm::X86::Tree::insertKeyDataTreeIntoLeaf> - Insert a new key/data/sub tree triple into a set of zmm registers if there is room, increment the length of the node and set the tree bit as indicated and increment the number of elements in the tree.

199 L<Nasm::X86::Tree::insertOneIntoTreeBits|/Nasm::X86::Tree::insertOneIntoTreeBits> - Insert a one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

200 L<Nasm::X86::Tree::insertTreeBit|/Nasm::X86::Tree::insertTreeBit> - Insert a zero or one into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

201 L<Nasm::X86::Tree::insertZeroIntoTreeBits|/Nasm::X86::Tree::insertZeroIntoTreeBits> - Insert a zero into the tree bits field in the numbered zmm at the specified point moving the bits at and beyond point one position to the right.

202 L<Nasm::X86::Tree::intersection|/Nasm::X86::Tree::intersection> - Given a tree of trees consider each sub tree as a set and form the intersection of all these sets as a new tree.

203 L<Nasm::X86::Tree::isTree|/Nasm::X86::Tree::isTree> - Set the Zero Flag to oppose the tree bit in the numbered zmm register holding the keys of a node to indicate whether the data element indicated by the specified register is an offset to a sub tree in the containing area or not.

204 L<Nasm::X86::Tree::lastNode|/Nasm::X86::Tree::lastNode> - Return as a variable the last node block in the specified tree node held in a zmm.

205 L<Nasm::X86::Tree::leafFromNodes|/Nasm::X86::Tree::leafFromNodes> - Return a variable containing true if we are on a leaf.

206 L<Nasm::X86::Tree::leftMost|/Nasm::X86::Tree::leftMost> - Return the offset of the left most node from the specified node.

207 L<Nasm::X86::Tree::leftOrRightMost|/Nasm::X86::Tree::leftOrRightMost> - Return the offset of the left most or right most node.

208 L<Nasm::X86::Tree::lengthFromKeys|/Nasm::X86::Tree::lengthFromKeys> - Get the length of the keys block in the numbered zmm and return it as a variable.

209 L<Nasm::X86::Tree::lengthIntoKeys|/Nasm::X86::Tree::lengthIntoKeys> - Get the length of the block in the numbered zmm from the specified variable.

210 L<Nasm::X86::Tree::maskForFullKeyArea|/Nasm::X86::Tree::maskForFullKeyArea> - Place a mask for the full key area in the numbered mask register.

211 L<Nasm::X86::Tree::maskForFullNodesArea|/Nasm::X86::Tree::maskForFullNodesArea> - Place a mask for the full nodes area in the numbered mask register.

212 L<Nasm::X86::Tree::merge|/Nasm::X86::Tree::merge> - Merge a left and right node if they are at minimum size.

213 L<Nasm::X86::Tree::mergeOrSteal|/Nasm::X86::Tree::mergeOrSteal> - Merge the block at the specified offset with its right sibling or steal from it.

214 L<Nasm::X86::Tree::nextNode|/Nasm::X86::Tree::nextNode> - Return as a variable the next node block offset after the specified one in the specified zmm.

215 L<Nasm::X86::Tree::optionsFromFirst|/Nasm::X86::Tree::optionsFromFirst> - Return a variable containing the options double word from the first block zmm register.

216 L<Nasm::X86::Tree::optionsIntoFirst|/Nasm::X86::Tree::optionsIntoFirst> - Put the contents of a variable into the options field of the first block of a tree  when the first block is held in a zmm register.

217 L<Nasm::X86::Tree::outAsUtf8|/Nasm::X86::Tree::outAsUtf8> - Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8.

218 L<Nasm::X86::Tree::outAsUtf8NL|/Nasm::X86::Tree::outAsUtf8NL> - Print the data values of the specified string on stdout assuming each data value is a utf32 character and that the output device supports utf8.

219 L<Nasm::X86::Tree::overWriteKeyDataTreeInLeaf|/Nasm::X86::Tree::overWriteKeyDataTreeInLeaf> - Over write an existing key/data/sub tree triple in a set of zmm registers and set the tree bit as indicated.

220 L<Nasm::X86::Tree::peek|/Nasm::X86::Tree::peek> - Peek at the element the specified distance back from the top of the stack and return its B<value> in data and found status in B<found> in the tree descriptor.

221 L<Nasm::X86::Tree::peekSubTree|/Nasm::X86::Tree::peekSubTree> - Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.

222 L<Nasm::X86::Tree::pop|/Nasm::X86::Tree::pop> - Pop the last value out of a tree and return the key/data/subTree in the tree descriptor.

223 L<Nasm::X86::Tree::popSubTree|/Nasm::X86::Tree::popSubTree> - Pop the last value out of a tree and return a tree descriptor positioned on it with the first/found fields set.

224 L<Nasm::X86::Tree::position|/Nasm::X86::Tree::position> - Create a new tree description for a tree positioned at the specified location.

225 L<Nasm::X86::Tree::prevNode|/Nasm::X86::Tree::prevNode> - Return as a variable the previous node block offset after the specified one in the specified zmm.

226 L<Nasm::X86::Tree::printInOrder|/Nasm::X86::Tree::printInOrder> - Print a tree in order.

227 L<Nasm::X86::Tree::push|/Nasm::X86::Tree::push> - Push a data value onto a tree.

228 L<Nasm::X86::Tree::put|/Nasm::X86::Tree::put> - Put a variable key and data into a tree.

229 L<Nasm::X86::Tree::putBlock|/Nasm::X86::Tree::putBlock> - Put a tree block held in three zmm registers back into the area holding the tree at the specified offset.

230 L<Nasm::X86::Tree::putKeyString|/Nasm::X86::Tree::putKeyString> - Associate a string of any length with a double word.

231 L<Nasm::X86::Tree::putLoop|/Nasm::X86::Tree::putLoop> - Set the value of the loop field from a variable.

232 L<Nasm::X86::Tree::relativeNode|/Nasm::X86::Tree::relativeNode> - Return as a variable a node offset relative (specified as ac constant) to another offset in the same node in the specified zmm.

233 L<Nasm::X86::Tree::replace|/Nasm::X86::Tree::replace> - Replace the key/data/subTree at the specified point in the specified zmm with the values found in the tree key/data/sub tree fields.

234 L<Nasm::X86::Tree::reverse|/Nasm::X86::Tree::reverse> - Create a clone of the string in reverse order.

235 L<Nasm::X86::Tree::rightMost|/Nasm::X86::Tree::rightMost> - Return the offset of the left most node from the specified node.

236 L<Nasm::X86::Tree::root|/Nasm::X86::Tree::root> - Check whether the specified offset refers to the root of a tree when the first block is held in a zmm register.

237 L<Nasm::X86::Tree::rootFromFirst|/Nasm::X86::Tree::rootFromFirst> - Return a variable containing the offset of the root block of a tree from the first block when held in a zmm register.

238 L<Nasm::X86::Tree::rootIntoFirst|/Nasm::X86::Tree::rootIntoFirst> - Put the contents of a variable into the root field of the first block of a tree when held in a zmm register.

239 L<Nasm::X86::Tree::setOrClearTreeBit|/Nasm::X86::Tree::setOrClearTreeBit> - Set or clear the tree bit selected by the specified point in the numbered zmm register holding the keys of a node to indicate that the data element indicated by the specified register is an offset to a sub tree in the containing area.

240 L<Nasm::X86::Tree::setOrClearTreeBitToMatchContent|/Nasm::X86::Tree::setOrClearTreeBitToMatchContent> - Set or clear the tree bit pointed to by the specified register depending on the content of the specified variable.

241 L<Nasm::X86::Tree::setTreeBit|/Nasm::X86::Tree::setTreeBit> - Set the tree bit in the numbered zmm register holding the keys of a node to indicate that the data element indexed by the specified register is an offset to a sub tree in the containing area.

242 L<Nasm::X86::Tree::setTreeBits|/Nasm::X86::Tree::setTreeBits> - Put the tree bits in the specified register into the numbered zmm.

243 L<Nasm::X86::Tree::size|/Nasm::X86::Tree::size> - Return in a variable the number of elements currently in the tree.

244 L<Nasm::X86::Tree::sizeFromFirst|/Nasm::X86::Tree::sizeFromFirst> - Return a variable containing the number of keys in the specified tree when the first block is held in a zmm register.

245 L<Nasm::X86::Tree::sizeIntoFirst|/Nasm::X86::Tree::sizeIntoFirst> - Put the contents of a variable into the size field of the first block of a tree  when the first block is held in a zmm register.

246 L<Nasm::X86::Tree::splitNode|/Nasm::X86::Tree::splitNode> - Split a node if it it is full returning a variable that indicates whether a split occurred or not.

247 L<Nasm::X86::Tree::splitNotRoot|/Nasm::X86::Tree::splitNotRoot> - Split a non root left node pushing its excess right and up.

248 L<Nasm::X86::Tree::splitRoot|/Nasm::X86::Tree::splitRoot> - Split a non root node into left and right nodes with the left half left in the left node and splitting key/data pushed into the parent node with the remainder pushed into the new right node.

249 L<Nasm::X86::Tree::stealFromLeft|/Nasm::X86::Tree::stealFromLeft> - Steal one key from the node on the left where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else  zero.

250 L<Nasm::X86::Tree::stealFromRight|/Nasm::X86::Tree::stealFromRight> - Steal one key from the node on the right where the current left node,parent node and right node are held in zmm registers and return one if the steal was performed, else zero.

251 L<Nasm::X86::Tree::substring|/Nasm::X86::Tree::substring> - Create the substring of the specified string between the specified start and end keys.

252 L<Nasm::X86::Tree::union|/Nasm::X86::Tree::union> - Given a tree of trees consider each sub tree as a set and form the union of all these sets as a new tree.

253 L<Nasm::X86::Tree::uniqueKeyString|/Nasm::X86::Tree::uniqueKeyString> - Add a key string to a string tree if the key is not already present and return a unique number identifying the string (although currently there is no way to fast way to recover the string from the number).

254 L<Nasm::X86::Tree::upFromData|/Nasm::X86::Tree::upFromData> - Up from the data zmm in a block in a tree.

255 L<Nasm::X86::Tree::upIntoData|/Nasm::X86::Tree::upIntoData> - Up into the data zmm in a block in a tree.

256 L<Nasm::X86::Tree::yb|/Nasm::X86::Tree::yb> - Call the specified block with each element of the specified tree in descending order.

257 L<Nasm::X86::Tree::zero|/Nasm::X86::Tree::zero> - Zero the return fields of a tree descriptor.

258 L<Nasm::X86::Unisyn::DescribeParse|/Nasm::X86::Unisyn::DescribeParse> - Describe a parse - create a description on the stack to receive the results of a parse

259 L<Nasm::X86::Unisyn::Lex::AlphabetsArray|/Nasm::X86::Unisyn::Lex::AlphabetsArray> - Create an array of utf32 to alphabet number.

260 L<Nasm::X86::Unisyn::Lex::composeUnisyn|/Nasm::X86::Unisyn::Lex::composeUnisyn> - Compose phrases of Unisyn and return them as a string

261 L<Nasm::X86::Unisyn::Lex::left     {3};|/Nasm::X86::Unisyn::Lex::left     {3};> - Left operand.

262 L<Nasm::X86::Unisyn::Lex::length   {1};|/Nasm::X86::Unisyn::Lex::length   {1};> - Length of the lexical item in bytes.

263 L<Nasm::X86::Unisyn::Lex::Letter::d|/Nasm::X86::Unisyn::Lex::Letter::d> - Dyad 2 - Double struck

264 L<Nasm::X86::Unisyn::Lex::Letter::e|/Nasm::X86::Unisyn::Lex::Letter::e> - Dyad 3 - Mono

265 L<Nasm::X86::Unisyn::Lex::Letter::f|/Nasm::X86::Unisyn::Lex::Letter::f> - Dyad 5 - Sans-serif Normal

266 L<Nasm::X86::Unisyn::Lex::Letter::g|/Nasm::X86::Unisyn::Lex::Letter::g> - Dyad 6 - Sans-serif Bold

267 L<Nasm::X86::Unisyn::Lex::Letter::h|/Nasm::X86::Unisyn::Lex::Letter::h> - Dyad 7  - Calligraphy - normal

268 L<Nasm::X86::Unisyn::Lex::Letter::i|/Nasm::X86::Unisyn::Lex::Letter::i> - Dyad 8 - Calligraphy - bold

269 L<Nasm::X86::Unisyn::Lex::Letter::j|/Nasm::X86::Unisyn::Lex::Letter::j> - Dyad 9 - Fraktur - Normal

270 L<Nasm::X86::Unisyn::Lex::Letter::k|/Nasm::X86::Unisyn::Lex::Letter::k> - Dyad 10 - Fraktur - bold

271 L<Nasm::X86::Unisyn::Lex::letterToNumber|/Nasm::X86::Unisyn::Lex::letterToNumber> - Map each letter in the union of the alphabets to a sequential number

272 L<Nasm::X86::Unisyn::Lex::Number::a {11}|/Nasm::X86::Unisyn::Lex::Number::a {11}> - Assign infix operator with right to left binding at priority 4.

273 L<Nasm::X86::Unisyn::Lex::Number::A {2}|/Nasm::X86::Unisyn::Lex::Number::A {2}> - ASCII characters extended with circled characters to act as escape sequences.

274 L<Nasm::X86::Unisyn::Lex::Number::b {7}|/Nasm::X86::Unisyn::Lex::Number::b {7}> - Open.

275 L<Nasm::X86::Unisyn::Lex::Number::B {8}|/Nasm::X86::Unisyn::Lex::Number::B {8}> - Close.

276 L<Nasm::X86::Unisyn::Lex::Number::F {1}|/Nasm::X86::Unisyn::Lex::Number::F {1}> - End symbol.

277 L<Nasm::X86::Unisyn::Lex::Number::l {18}|/Nasm::X86::Unisyn::Lex::Number::l {18}> - Dyad 11

278 L<Nasm::X86::Unisyn::Lex::Number::m {19}|/Nasm::X86::Unisyn::Lex::Number::m {19}> - Dyad 12

279 L<Nasm::X86::Unisyn::Lex::Number::p {3}|/Nasm::X86::Unisyn::Lex::Number::p {3}> - Prefix operator - applies only to the following variable or bracketed term.

280 L<Nasm::X86::Unisyn::Lex::Number::q {5}|/Nasm::X86::Unisyn::Lex::Number::q {5}> - Suffix operator - applies only to the preceding variable or bracketed term.

281 L<Nasm::X86::Unisyn::Lex::Number::S {0}|/Nasm::X86::Unisyn::Lex::Number::S {0}> - Start symbol.

282 L<Nasm::X86::Unisyn::Lex::Number::s {6}|/Nasm::X86::Unisyn::Lex::Number::s {6}> - Infix operator with left to right binding at priority 1.

283 L<Nasm::X86::Unisyn::Lex::Number::v {4}|/Nasm::X86::Unisyn::Lex::Number::v {4}> - Variable names.

284 L<Nasm::X86::Unisyn::Lex::Number::w {20}|/Nasm::X86::Unisyn::Lex::Number::w {20}> - White space

285 L<Nasm::X86::Unisyn::Lex::numberToLetter|/Nasm::X86::Unisyn::Lex::numberToLetter> - Recover a letter from its unique number

286 L<Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray|/Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray> - Create and load the table of lexical transitions.

287 L<Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits {5}|/Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits {5}> - The number of bits needed to express a transition

288 L<Nasm::X86::Unisyn::Lex::position {0};|/Nasm::X86::Unisyn::Lex::position {0};> - Position of the parsed item in the input text.

289 L<Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};|/Nasm::X86::Unisyn::Lex::Reason::BadUtf8           {1};> - Bad utf8 character encountered.

290 L<Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};|/Nasm::X86::Unisyn::Lex::Reason::BracketsNotClosed {7};> - Open brackets not closed at end of.

291 L<Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};|/Nasm::X86::Unisyn::Lex::Reason::InvalidChar       {2};> - Character not part of Earl Zero.

292 L<Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};|/Nasm::X86::Unisyn::Lex::Reason::InvalidTransition {3};> - Transition from one lexical item to another not allowed.

293 L<Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};|/Nasm::X86::Unisyn::Lex::Reason::Mismatch          {5};> - Mismatched bracket.

294 L<Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};|/Nasm::X86::Unisyn::Lex::Reason::NotFinal          {6};> - Expected something after final character.

295 L<Nasm::X86::Unisyn::Lex::Reason::Success           {0};|/Nasm::X86::Unisyn::Lex::Reason::Success           {0};> - Successful parse.

296 L<Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};|/Nasm::X86::Unisyn::Lex::Reason::TrailingClose     {4};> - Trailing closing bracket discovered.

297 L<Nasm::X86::Unisyn::Lex::right    {4};|/Nasm::X86::Unisyn::Lex::right    {4};> - Right operand.

298 L<Nasm::X86::Unisyn::Lex::symbol   {5};|/Nasm::X86::Unisyn::Lex::symbol   {5};> - Symbol.

299 L<Nasm::X86::Unisyn::Lex::type     {2};|/Nasm::X86::Unisyn::Lex::type     {2};> - Type of the lexical item.

300 L<Nasm::X86::Unisyn::Parse|/Nasm::X86::Unisyn::Parse> - Parse a string of utf8 characters.

301 L<Nasm::X86::Unisyn::Parse::dump|/Nasm::X86::Unisyn::Parse::dump> - Dump a parse tree in order.

302 L<Nasm::X86::Unisyn::Parse::dumpParseResult|/Nasm::X86::Unisyn::Parse::dumpParseResult> - Dump the result of a parse

303 L<Nasm::X86::Unisyn::Parse::dumpPostOrder|/Nasm::X86::Unisyn::Parse::dumpPostOrder> - Dump a parse tree in post order.

304 L<Nasm::X86::Unisyn::Parse::traverseApplyingLibraryOperators|/Nasm::X86::Unisyn::Parse::traverseApplyingLibraryOperators> - Traverse a parse tree, in post order, applying a library of operators.

305 L<Nasm::X86::Variable::add|/Nasm::X86::Variable::add> - Add the right hand variable to the left hand variable and return the result as a new variable.

306 L<Nasm::X86::Variable::address|/Nasm::X86::Variable::address> - Create a variable that contains the address of another variable.

307 L<Nasm::X86::Variable::addressExpr|/Nasm::X86::Variable::addressExpr> - Create a register expression to address an offset form a variable.

308 L<Nasm::X86::Variable::allocateMemory|/Nasm::X86::Variable::allocateMemory> - Allocate a variable amount of memory via mmap and return its address.

309 L<Nasm::X86::Variable::and|/Nasm::X86::Variable::and> - And two variables.

310 L<Nasm::X86::Variable::arithmetic|/Nasm::X86::Variable::arithmetic> - Return a variable containing the result of an arithmetic operation on the left hand and right hand side variables.

311 L<Nasm::X86::Variable::assign|/Nasm::X86::Variable::assign> - Assign to the left hand side the value of the right hand side.

312 L<Nasm::X86::Variable::at|/Nasm::X86::Variable::at> - Return a "[register expression]" to address the data in the variable in the current stack frame.

313 L<Nasm::X86::Variable::bFromZ|/Nasm::X86::Variable::bFromZ> - Get the byte from the numbered zmm register and put it in a variable.

314 L<Nasm::X86::Variable::bIntoX|/Nasm::X86::Variable::bIntoX> - Place the value of the content variable at the byte in the numbered xmm register.

315 L<Nasm::X86::Variable::bIntoZ|/Nasm::X86::Variable::bIntoZ> - Place the value of the content variable at the byte in the numbered zmm register.

316 L<Nasm::X86::Variable::booleanZF|/Nasm::X86::Variable::booleanZF> - Combine the left hand variable with the right hand variable via a boolean operator and indicate the result by setting the zero flag if the result is true.

317 L<Nasm::X86::Variable::clearBit|/Nasm::X86::Variable::clearBit> - Clear a bit in the specified mask register retaining the other bits.

318 L<Nasm::X86::Variable::clearMaskBit|/Nasm::X86::Variable::clearMaskBit> - Clear a bit in the specified mask register retaining the other bits.

319 L<Nasm::X86::Variable::clearMemory|/Nasm::X86::Variable::clearMemory> - Clear the memory described in this variable.

320 L<Nasm::X86::Variable::clone|/Nasm::X86::Variable::clone> - Clone a variable to make a new variable.

321 L<Nasm::X86::Variable::copy|/Nasm::X86::Variable::copy> - Copy one variable into another.

322 L<Nasm::X86::Variable::copyMemory|/Nasm::X86::Variable::copyMemory> - Copy from one block of memory to another.

323 L<Nasm::X86::Variable::copyRef|/Nasm::X86::Variable::copyRef> - Copy a reference to a variable.

324 L<Nasm::X86::Variable::copyZF|/Nasm::X86::Variable::copyZF> - Copy the current state of the zero flag into a variable.

325 L<Nasm::X86::Variable::copyZFInverted|/Nasm::X86::Variable::copyZFInverted> - Copy the opposite of the current state of the zero flag into a variable.

326 L<Nasm::X86::Variable::d|/Nasm::X86::Variable::d> - Dump the value of a variable on stderr and append the source file calling line in a format that Geany understands.

327 L<Nasm::X86::Variable::dec|/Nasm::X86::Variable::dec> - Decrement a variable.

328 L<Nasm::X86::Variable::dereference|/Nasm::X86::Variable::dereference> - Create a variable that contains the contents of the variable addressed by the specified variable.

329 L<Nasm::X86::Variable::dFromPointInZ|/Nasm::X86::Variable::dFromPointInZ> - Get the double word from the numbered zmm register at a point specified by the variable and return it in a variable.

330 L<Nasm::X86::Variable::dFromZ|/Nasm::X86::Variable::dFromZ> - Get the double word from the numbered zmm register and put it in a variable.

331 L<Nasm::X86::Variable::dIntoPointInZ|/Nasm::X86::Variable::dIntoPointInZ> - Put the variable double word content into the numbered zmm register at a point specified by the variable.

332 L<Nasm::X86::Variable::dIntoX|/Nasm::X86::Variable::dIntoX> - Place the value of the content variable at the double word in the numbered xmm register.

333 L<Nasm::X86::Variable::dIntoZ|/Nasm::X86::Variable::dIntoZ> - Place the value of the content variable at the double word in the numbered zmm register.

334 L<Nasm::X86::Variable::divide|/Nasm::X86::Variable::divide> - Divide the left hand variable by the right hand variable and return the result as a new variable.

335 L<Nasm::X86::Variable::division|/Nasm::X86::Variable::division> - Return a variable containing the result or the remainder that occurs when the left hand side is divided by the right hand side.

336 L<Nasm::X86::Variable::dump|/Nasm::X86::Variable::dump> - Dump the value of a variable to the specified channel adding an optional title and new line if requested.

337 L<Nasm::X86::Variable::eq|/Nasm::X86::Variable::eq> - Check whether the left hand variable is equal to the right hand variable.

338 L<Nasm::X86::Variable::equals|/Nasm::X86::Variable::equals> - Equals operator.

339 L<Nasm::X86::Variable::err|/Nasm::X86::Variable::err> - Dump the value of a variable on stderr.

340 L<Nasm::X86::Variable::errCString|/Nasm::X86::Variable::errCString> - Print a zero terminated C style string addressed by a variable on stderr.

341 L<Nasm::X86::Variable::errCStringNL|/Nasm::X86::Variable::errCStringNL> - Print a zero terminated C style string addressed by a variable on stderr followed by a new line.

342 L<Nasm::X86::Variable::errInDec|/Nasm::X86::Variable::errInDec> - Dump the value of a variable on stderr in decimal.

343 L<Nasm::X86::Variable::errInDecNL|/Nasm::X86::Variable::errInDecNL> - Dump the value of a variable on stderr in decimal followed by a new line.

344 L<Nasm::X86::Variable::errNL|/Nasm::X86::Variable::errNL> - Dump the value of a variable on stderr and append a new line.

345 L<Nasm::X86::Variable::errRightInBin|/Nasm::X86::Variable::errRightInBin> - Write the specified variable number in binary right justified in a field of specified width to stderr.

346 L<Nasm::X86::Variable::errRightInBinNL|/Nasm::X86::Variable::errRightInBinNL> - Write the specified variable number in binary right justified in a field of specified width to stderr followed by a new line.

347 L<Nasm::X86::Variable::errRightInDec|/Nasm::X86::Variable::errRightInDec> - Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width.

348 L<Nasm::X86::Variable::errRightInDecNL|/Nasm::X86::Variable::errRightInDecNL> - Dump the value of a variable on stderr as a decimal number right adjusted in a field of specified width followed by a new line.

349 L<Nasm::X86::Variable::errRightInHex|/Nasm::X86::Variable::errRightInHex> - Write the specified variable number in hexadecimal right justified in a field of specified width to stderr.

350 L<Nasm::X86::Variable::errRightInHexNL|/Nasm::X86::Variable::errRightInHexNL> - Write the specified variable number in hexadecimal right justified in a field of specified width to stderr followed by a new line.

351 L<Nasm::X86::Variable::errSpaces|/Nasm::X86::Variable::errSpaces> - Print the specified number of spaces to stderr.

352 L<Nasm::X86::Variable::for|/Nasm::X86::Variable::for> - Iterate a block a variable number of times.

353 L<Nasm::X86::Variable::freeMemory|/Nasm::X86::Variable::freeMemory> - Free the memory addressed by this variable for the specified length.

354 L<Nasm::X86::Variable::ge|/Nasm::X86::Variable::ge> - Check whether the left hand variable is greater than or equal to the right hand variable.

355 L<Nasm::X86::Variable::getReg|/Nasm::X86::Variable::getReg> - Load the variable from a register expression.

356 L<Nasm::X86::Variable::gt|/Nasm::X86::Variable::gt> - Check whether the left hand variable is greater than the right hand variable.

357 L<Nasm::X86::Variable::inc|/Nasm::X86::Variable::inc> - Increment a variable.

358 L<Nasm::X86::Variable::incDec|/Nasm::X86::Variable::incDec> - Increment or decrement a variable.

359 L<Nasm::X86::Variable::isRef|/Nasm::X86::Variable::isRef> - Check whether the specified  variable is a reference to another variable.

360 L<Nasm::X86::Variable::le|/Nasm::X86::Variable::le> - Check whether the left hand variable is less than or equal to the right hand variable.

361 L<Nasm::X86::Variable::loadZmm|/Nasm::X86::Variable::loadZmm> - Load bytes from the memory addressed by the specified source variable into the numbered zmm register.

362 L<Nasm::X86::Variable::lt|/Nasm::X86::Variable::lt> - Check whether the left hand variable is less than the right hand variable.

363 L<Nasm::X86::Variable::max|/Nasm::X86::Variable::max> - Maximum of two variables.

364 L<Nasm::X86::Variable::min|/Nasm::X86::Variable::min> - Minimum of two variables.

365 L<Nasm::X86::Variable::minusAssign|/Nasm::X86::Variable::minusAssign> - Implement minus and assign.

366 L<Nasm::X86::Variable::mod|/Nasm::X86::Variable::mod> - Divide the left hand variable by the right hand variable and return the remainder as a new variable.

367 L<Nasm::X86::Variable::ne|/Nasm::X86::Variable::ne> - Check whether the left hand variable is not equal to the right hand variable.

368 L<Nasm::X86::Variable::not|/Nasm::X86::Variable::not> - Form two complement of left hand side and return it as a variable.

369 L<Nasm::X86::Variable::or|/Nasm::X86::Variable::or> - Or two variables.

370 L<Nasm::X86::Variable::out|/Nasm::X86::Variable::out> - Dump the value of a variable on stdout.

371 L<Nasm::X86::Variable::outCString|/Nasm::X86::Variable::outCString> - Print a zero terminated C style string addressed by a variable on stdout.

372 L<Nasm::X86::Variable::outCStringNL|/Nasm::X86::Variable::outCStringNL> - Print a zero terminated C style string addressed by a variable on stdout followed by a new line.

373 L<Nasm::X86::Variable::outInDec|/Nasm::X86::Variable::outInDec> - Dump the value of a variable on stdout in decimal.

374 L<Nasm::X86::Variable::outInDecNL|/Nasm::X86::Variable::outInDecNL> - Dump the value of a variable on stdout in decimal followed by a new line.

375 L<Nasm::X86::Variable::outNL|/Nasm::X86::Variable::outNL> - Dump the value of a variable on stdout and append a new line.

376 L<Nasm::X86::Variable::outRightInBin|/Nasm::X86::Variable::outRightInBin> - Write the specified variable number in binary right justified in a field of specified width to stdout.

377 L<Nasm::X86::Variable::outRightInBinNL|/Nasm::X86::Variable::outRightInBinNL> - Write the specified variable number in binary right justified in a field of specified width to stdout followed by a new line.

378 L<Nasm::X86::Variable::outRightInDec|/Nasm::X86::Variable::outRightInDec> - Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width.

379 L<Nasm::X86::Variable::outRightInDecNL|/Nasm::X86::Variable::outRightInDecNL> - Dump the value of a variable on stdout as a decimal number right adjusted in a field of specified width followed by a new line.

380 L<Nasm::X86::Variable::outRightInHex|/Nasm::X86::Variable::outRightInHex> - Write the specified variable number in hexadecimal right justified in a field of specified width to stdout.

381 L<Nasm::X86::Variable::outRightInHexNL|/Nasm::X86::Variable::outRightInHexNL> - Write the specified variable number in hexadecimal right justified in a field of specified width to stdout followed by a new line.

382 L<Nasm::X86::Variable::outSpaces|/Nasm::X86::Variable::outSpaces> - Print the specified number of spaces to stdout.

383 L<Nasm::X86::Variable::plusAssign|/Nasm::X86::Variable::plusAssign> - Implement plus and assign.

384 L<Nasm::X86::Variable::printErrMemory|/Nasm::X86::Variable::printErrMemory> - Print the specified number of bytes of the memory addressed by the variable on stdout.

385 L<Nasm::X86::Variable::printErrMemoryInHexNL|/Nasm::X86::Variable::printErrMemoryInHexNL> - Write the memory addressed by a variable to stderr.

386 L<Nasm::X86::Variable::printErrMemoryNL|/Nasm::X86::Variable::printErrMemoryNL> - Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.

387 L<Nasm::X86::Variable::printMemory|/Nasm::X86::Variable::printMemory> - Print the specified number of bytes from the memory addressed by the variable on the specified channel.

388 L<Nasm::X86::Variable::printMemoryInHexNL|/Nasm::X86::Variable::printMemoryInHexNL> - Write, in hexadecimal, the memory addressed by a variable to stdout or stderr.

389 L<Nasm::X86::Variable::printOutMemory|/Nasm::X86::Variable::printOutMemory> - Print the specified number of bytes of the memory addressed by the variable on stdout.

390 L<Nasm::X86::Variable::printOutMemoryInHexNL|/Nasm::X86::Variable::printOutMemoryInHexNL> - Write the memory addressed by a variable to stdout.

391 L<Nasm::X86::Variable::printOutMemoryNL|/Nasm::X86::Variable::printOutMemoryNL> - Print the specified number of bytes of the memory addressed by the variable on stdout followed by a new line.

392 L<Nasm::X86::Variable::putBwdqIntoMm|/Nasm::X86::Variable::putBwdqIntoMm> - Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register.

393 L<Nasm::X86::Variable::putWIntoZmm|/Nasm::X86::Variable::putWIntoZmm> - Place the value of the content variable at the word in the numbered zmm register.

394 L<Nasm::X86::Variable::qFromZ|/Nasm::X86::Variable::qFromZ> - Get the quad word from the numbered zmm register and put it in a variable.

395 L<Nasm::X86::Variable::qIntoX|/Nasm::X86::Variable::qIntoX> - Place the value of the content variable at the quad word in the numbered xmm register.

396 L<Nasm::X86::Variable::qIntoZ|/Nasm::X86::Variable::qIntoZ> - Place the value of the content variable at the quad word in the numbered zmm register.

397 L<Nasm::X86::Variable::rightInBin|/Nasm::X86::Variable::rightInBin> - Write the specified variable number in binary right justified in a field of specified width to the specified channel.

398 L<Nasm::X86::Variable::rightInDec|/Nasm::X86::Variable::rightInDec> - Dump the value of a variable on the specified channel as a decimal  number right adjusted in a field of specified width.

399 L<Nasm::X86::Variable::rightInHex|/Nasm::X86::Variable::rightInHex> - Write the specified variable number in hexadecimal right justified in a field of specified width to the specified channel.

400 L<Nasm::X86::Variable::setBit|/Nasm::X86::Variable::setBit> - Set a bit in the specified register retaining the other bits.

401 L<Nasm::X86::Variable::setMask|/Nasm::X86::Variable::setMask> - Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.

402 L<Nasm::X86::Variable::setMaskBit|/Nasm::X86::Variable::setMaskBit> - Set a bit in the specified mask register retaining the other bits.

403 L<Nasm::X86::Variable::setMaskFirst|/Nasm::X86::Variable::setMaskFirst> - Set the first bits in the specified mask register.

404 L<Nasm::X86::Variable::setReg|/Nasm::X86::Variable::setReg> - Set the named registers from the content of the variable.

405 L<Nasm::X86::Variable::setZmm|/Nasm::X86::Variable::setZmm> - Load bytes from the memory addressed by specified source variable into the numbered zmm register at the offset in the specified offset moving the number of bytes in the specified variable.

406 L<Nasm::X86::Variable::shiftLeft|/Nasm::X86::Variable::shiftLeft> - Shift the left hand variable left by the number of bits specified in the right hand variable and return the result as a new variable.

407 L<Nasm::X86::Variable::shiftRight|/Nasm::X86::Variable::shiftRight> - Shift the left hand variable right by the number of bits specified in the right hand variable and return the result as a new variable.

408 L<Nasm::X86::Variable::spaces|/Nasm::X86::Variable::spaces> - Print the specified number of spaces to the specified channel.

409 L<Nasm::X86::Variable::str|/Nasm::X86::Variable::str> - The name of the variable.

410 L<Nasm::X86::Variable::sub|/Nasm::X86::Variable::sub> - Subtract the right hand variable from the left hand variable and return the result as a new variable.

411 L<Nasm::X86::Variable::times|/Nasm::X86::Variable::times> - Multiply the left hand variable by the right hand variable and return the result as a new variable.

412 L<Nasm::X86::Variable::update|/Nasm::X86::Variable::update> - Update the content of the addressed variable with the content of the specified variable.

413 L<Nasm::X86::Variable::wFromZ|/Nasm::X86::Variable::wFromZ> - Get the word from the numbered zmm register and put it in a variable.

414 L<Nasm::X86::Variable::wIntoX|/Nasm::X86::Variable::wIntoX> - Place the value of the content variable at the word in the numbered xmm register.

415 L<Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2}|/Nasm::X86::Yggdrasil::SubroutineDefinitions{K key => 2}> - Maps the unique string number for a subroutine name to the offset in the are that contains the length (as a dword) followed by the string content of the Perl data structure describing the subroutine in question.

416 L<Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1}|/Nasm::X86::Yggdrasil::SubroutineOffsets    {K key => 1}> - Translates a string number into the offset of a subroutine in an area.

417 L<Nasm::X86::Yggdrasil::UniqueStrings        {K key => 0}|/Nasm::X86::Yggdrasil::UniqueStrings        {K key => 0}> - A tree of strings that assigns unique numbers to strings.

418 L<Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3}|/Nasm::X86::Yggdrasil::Unisyn::Alphabets    {K key => 3}> - Unisyn alphabets.

419 L<Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5}|/Nasm::X86::Yggdrasil::Unisyn::Close        {K key => 5}> - Close bracket to open bracket

420 L<Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4}|/Nasm::X86::Yggdrasil::Unisyn::Open         {K key => 4}> - Open bracket to close bracket

421 L<Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6}|/Nasm::X86::Yggdrasil::Unisyn::Transitions  {K key => 6}> - Permissible transitions from alphabet to alphabet

422 L<numberWithUnderScores|/numberWithUnderScores> - Place underscores in the string representation of a number.

423 L<onGitHub|/onGitHub> - Whether we are on GitHub or not.

424 L<OnSegv|/OnSegv> - Request a trace back followed by exit on a B<segv> signal.

425 L<OpenRead|/OpenRead> - Open a file, whose name is addressed by rax, for read and return the file descriptor in rax.

426 L<OpenWrite|/OpenWrite> - Create the file named by the terminated string addressed by rax for write.

427 L<opposingJump|/opposingJump> - Return the opposite of a jump.

428 L<OptimizePopPush|/OptimizePopPush> - Perform code optimizations.

429 L<OptimizeReload|/OptimizeReload> - Reload: a = b; b = a;  remove second - as redundant.

430 L<OrBlock|/OrBlock> - Short circuit B<or>: execute a block of code to test conditions which, if one of them is met, leads on to the execution of the pass block, if all of the tests fail we continue withe the test block.

431 L<ParseUnisyn|/ParseUnisyn> - Parse a string of utf8 characters.

432 L<Pass|/Pass> - Pass block for an L<OrBlock>.

433 L<PopR|/PopR> - Pop registers from the stack.

434 L<PopRR|/PopRR> - Pop registers from the stack without tracking.

435 L<PrintCString|/PrintCString> - Print a zero terminated C style string addressed by a variable on the specified channel.

436 L<PrintCStringNL|/PrintCStringNL> - Print a zero terminated C style string addressed by a variable on the specified channel followed by a new line.

437 L<PrintErrMemory|/PrintErrMemory> - Print the memory addressed by rax for a length of rdi on stderr.

438 L<PrintErrMemory_InHex|/PrintErrMemory_InHex> - Dump memory from the address in rax for the length in rdi on stderr.

439 L<PrintErrMemory_InHexNL|/PrintErrMemory_InHexNL> - Dump memory from the address in rax for the length in rdi and then print a new line.

440 L<PrintErrMemoryInHex|/PrintErrMemoryInHex> - Dump memory from the address in rax for the length in rdi on stderr.

441 L<PrintErrMemoryInHexNL|/PrintErrMemoryInHexNL> - Dump memory from the address in rax for the length in rdi and then print a new line.

442 L<PrintErrMemoryNL|/PrintErrMemoryNL> - Print the memory addressed by rax for a length of rdi followed by a new line on stderr.

443 L<PrintErrNL|/PrintErrNL> - Print a new line to stderr.

444 L<PrintErrOneRegisterInHex|/PrintErrOneRegisterInHex> - Print the named register as a hex string on stderr.

445 L<PrintErrOneRegisterInHexNL|/PrintErrOneRegisterInHexNL> - Print the named register as a hex string on stderr followed by new line.

446 L<PrintErrRax_InHex|/PrintErrRax_InHex> - Write the content of register rax in hexadecimal in big endian notation to stderr.

447 L<PrintErrRax_InHexNL|/PrintErrRax_InHexNL> - Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.

448 L<PrintErrRaxAsChar|/PrintErrRaxAsChar> - Print the character in rax on stderr.

449 L<PrintErrRaxAsCharNL|/PrintErrRaxAsCharNL> - Print the character in rax on stderr followed by a new line.

450 L<PrintErrRaxAsText|/PrintErrRaxAsText> - Print rax as text on stderr.

451 L<PrintErrRaxAsTextNL|/PrintErrRaxAsTextNL> - Print rax as text on stderr followed by a new line.

452 L<PrintErrRaxInDec|/PrintErrRaxInDec> - Print rax in decimal on stderr.

453 L<PrintErrRaxInDecNL|/PrintErrRaxInDecNL> - Print rax in decimal on stderr followed by a new line.

454 L<PrintErrRaxInHex|/PrintErrRaxInHex> - Write the content of register rax in hexadecimal in big endian notation to stderr.

455 L<PrintErrRaxInHexNL|/PrintErrRaxInHexNL> - Write the content of register rax in hexadecimal in big endian notation to stderr followed by a new line.

456 L<PrintErrRegisterInHex|/PrintErrRegisterInHex> - Print the named registers as hex strings on stderr.

457 L<PrintErrRightInBin|/PrintErrRightInBin> - Write the specified variable in binary right justified in a field of specified width on stderr.

458 L<PrintErrRightInBinNL|/PrintErrRightInBinNL> - Write the specified variable in binary right justified in a field of specified width on stderr followed by a new line.

459 L<PrintErrRightInDec|/PrintErrRightInDec> - Print a variable or register in decimal right justified in a field of the specified width on stderr.

460 L<PrintErrRightInDecNL|/PrintErrRightInDecNL> - Print a variable or register in decimal right justified in a field of the specified width on stderr followed by a new line.

461 L<PrintErrRightInHex|/PrintErrRightInHex> - Write the specified variable in hexadecimal right justified in a field of specified width on stderr.

462 L<PrintErrRightInHexNL|/PrintErrRightInHexNL> - Write the specified variable in hexadecimal right justified in a field of specified width on stderr followed by a new line.

463 L<PrintErrSpace|/PrintErrSpace> - Print  a constant number of spaces to stderr.

464 L<PrintErrString|/PrintErrString> - Print a constant string to stderr.

465 L<PrintErrStringNL|/PrintErrStringNL> - Print a constant string to stderr followed by a new line.

466 L<PrintErrTraceBack|/PrintErrTraceBack> - Print sub routine track back on stderr and then exit with a message.

467 L<PrintErrZF|/PrintErrZF> - Print the zero flag without disturbing it on stderr.

468 L<PrintMemory|/PrintMemory> - Print the memory addressed by rax for a length of rdi on the specified channel where channel can be a constant number or a register expression using a bound register.

469 L<PrintMemory_InHex|/PrintMemory_InHex> - Dump memory from the address in rax for the length in rdi on the specified channel.

470 L<PrintMemoryInHex|/PrintMemoryInHex> - Dump memory from the address in rax for the length in rdi on the specified channel.

471 L<PrintMemoryNL|/PrintMemoryNL> - Print the memory addressed by rax for a length of rdi on the specified channel followed by a new line.

472 L<PrintNL|/PrintNL> - Print a new line to stdout  or stderr.

473 L<PrintOneRegisterInHex|/PrintOneRegisterInHex> - Print the named register as a hex string.

474 L<PrintOutMemory|/PrintOutMemory> - Print the memory addressed by rax for a length of rdi on stdout.

475 L<PrintOutMemory_InHex|/PrintOutMemory_InHex> - Dump memory from the address in rax for the length in rdi on stdout.

476 L<PrintOutMemory_InHexNL|/PrintOutMemory_InHexNL> - Dump memory from the address in rax for the length in rdi and then print a new line.

477 L<PrintOutMemoryInHex|/PrintOutMemoryInHex> - Dump memory from the address in rax for the length in rdi on stdout.

478 L<PrintOutMemoryInHexNL|/PrintOutMemoryInHexNL> - Dump memory from the address in rax for the length in rdi and then print a new line.

479 L<PrintOutMemoryNL|/PrintOutMemoryNL> - Print the memory addressed by rax for a length of rdi followed by a new line on stdout.

480 L<PrintOutNL|/PrintOutNL> - Print a new line to stderr.

481 L<PrintOutOneRegisterInHex|/PrintOutOneRegisterInHex> - Print the named register as a hex string on stdout.

482 L<PrintOutOneRegisterInHexNL|/PrintOutOneRegisterInHexNL> - Print the named register as a hex string on stdout followed by new line.

483 L<PrintOutRax_InHex|/PrintOutRax_InHex> - Write the content of register rax in hexadecimal in big endian notation to stout.

484 L<PrintOutRax_InHexNL|/PrintOutRax_InHexNL> - Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.

485 L<PrintOutRaxAsChar|/PrintOutRaxAsChar> - Print the character in rax on stdout.

486 L<PrintOutRaxAsCharNL|/PrintOutRaxAsCharNL> - Print the character in rax on stdout followed by a new line.

487 L<PrintOutRaxAsText|/PrintOutRaxAsText> - Print rax as text on stdout.

488 L<PrintOutRaxAsTextNL|/PrintOutRaxAsTextNL> - Print rax as text on stdout followed by a new line.

489 L<PrintOutRaxInDec|/PrintOutRaxInDec> - Print rax in decimal on stdout.

490 L<PrintOutRaxInDecNL|/PrintOutRaxInDecNL> - Print rax in decimal on stdout followed by a new line.

491 L<PrintOutRaxInHex|/PrintOutRaxInHex> - Write the content of register rax in hexadecimal in big endian notation to stout.

492 L<PrintOutRaxInHexNL|/PrintOutRaxInHexNL> - Write the content of register rax in hexadecimal in big endian notation to stdout followed by a new line.

493 L<PrintOutRaxInReverseInHex|/PrintOutRaxInReverseInHex> - Write the content of register rax to stderr in hexadecimal in little endian notation.

494 L<PrintOutRegisterInHex|/PrintOutRegisterInHex> - Print the named registers as hex strings on stdout.

495 L<PrintOutRegistersInHex|/PrintOutRegistersInHex> - Print the general purpose registers in hex.

496 L<PrintOutRflagsInHex|/PrintOutRflagsInHex> - Print the flags register in hex.

497 L<PrintOutRightInBin|/PrintOutRightInBin> - Write the specified variable in binary right justified in a field of specified width on stdout.

498 L<PrintOutRightInBinNL|/PrintOutRightInBinNL> - Write the specified variable in binary right justified in a field of specified width on stdout followed by a new line.

499 L<PrintOutRightInDec|/PrintOutRightInDec> - Print a variable or register in decimal right justified in a field of the specified width on stdout.

500 L<PrintOutRightInDecNL|/PrintOutRightInDecNL> - Print a variable or register in decimal right justified in a field of the specified width on stdout followed by a new line.

501 L<PrintOutRightInHex|/PrintOutRightInHex> - Write the specified variable in hexadecimal right justified in a field of specified width on stdout.

502 L<PrintOutRightInHexNL|/PrintOutRightInHexNL> - Write the specified variable in hexadecimal right justified in a field of specified width on stdout followed by a new line.

503 L<PrintOutRipInHex|/PrintOutRipInHex> - Print the instruction pointer in hex.

504 L<PrintOutSpace|/PrintOutSpace> - Print a constant number of spaces to stdout.

505 L<PrintOutString|/PrintOutString> - Print a constant string to stdout.

506 L<PrintOutStringNL|/PrintOutStringNL> - Print a constant string to stdout followed by a new line.

507 L<PrintOutTraceBack|/PrintOutTraceBack> - Print sub routine track back on stdout and then exit with a message.

508 L<PrintOutZF|/PrintOutZF> - Print the zero flag without disturbing it on stdout.

509 L<PrintRax_InHex|/PrintRax_InHex> - Write the content of register rax in hexadecimal in big endian notation to the specified channel replacing zero bytes with __.

510 L<PrintRaxAsChar|/PrintRaxAsChar> - Print the ascii character in rax on the specified channel.

511 L<PrintRaxAsText|/PrintRaxAsText> - Print the string in rax on the specified channel.

512 L<PrintRaxInDec|/PrintRaxInDec> - Print rax in decimal on the specified channel.

513 L<PrintRaxInHex|/PrintRaxInHex> - Write the content of register rax in hexadecimal in big endian notation to the specified channel.

514 L<PrintRaxRightInDec|/PrintRaxRightInDec> - Print rax in decimal right justified in a field of the specified width on the specified channel.

515 L<PrintRegisterInHex|/PrintRegisterInHex> - Print the named registers as hex strings.

516 L<PrintRightInBin|/PrintRightInBin> - Print out a number in binary right justified in a field of specified width on the specified channel.

517 L<PrintRightInDec|/PrintRightInDec> - Print out a number in decimal right justified in a field of specified width on the specified channel.

518 L<PrintRightInHex|/PrintRightInHex> - Print out a number in hex right justified in a field of specified width on the specified channel.

519 L<PrintSpace|/PrintSpace> - Print a constant number of spaces to the specified channel.

520 L<PrintString|/PrintString> - Print a constant string to the specified channel.

521 L<PrintStringNL|/PrintStringNL> - Print a constant string to the specified channel followed by a new line.

522 L<PrintTraceBack|/PrintTraceBack> - Trace the call stack.

523 L<PushR|/PushR> - Push registers onto the stack.

524 L<PushRR|/PushRR> - Push registers onto the stack without tracking.

525 L<qFromX|/qFromX> - Get the quad word from the numbered xmm register and return it in a variable.

526 L<qFromZ|/qFromZ> - Get the quad word from the numbered zmm register and return it in a variable.

527 L<R|/R> - Define a reference variable.

528 L<Rb|/Rb> - Layout bytes in the data segment and return their label.

529 L<Rbwdq|/Rbwdq> - Layout data.

530 L<Rd|/Rd> - Layout double words in the data segment and return their label.

531 L<ReadArea|/ReadArea> - Read an area stored in a file into memory and return an area descriptor for the area so created.

532 L<ReadChar|/ReadChar> - Read a character from stdin and return it in rax else return -1 in rax if no character was read.

533 L<ReadFile|/ReadFile> - Read a file into memory.

534 L<ReadInteger|/ReadInteger> - Reads an integer in decimal and returns it in rax.

535 L<ReadLine|/ReadLine> - Reads up to 8 characters followed by a terminating return and place them into rax.

536 L<ReadTimeStampCounter|/ReadTimeStampCounter> - Read the time stamp counter and return the time in nanoseconds in rax.

537 L<registerNameFromNumber|/registerNameFromNumber> - Register name from number where possible.

538 L<RegisterSize|/RegisterSize> - Return the size of a register.

539 L<RestoreFirstFour|/RestoreFirstFour> - Restore the first 4 parameter registers.

540 L<RestoreFirstFourExceptRax|/RestoreFirstFourExceptRax> - Restore the first 4 parameter registers except rax so it can return its value.

541 L<RestoreFirstSeven|/RestoreFirstSeven> - Restore the first 7 parameter registers.

542 L<RestoreFirstSevenExceptRax|/RestoreFirstSevenExceptRax> - Restore the first 7 parameter registers except rax which is being used to return the result.

543 L<Rq|/Rq> - Layout quad words in the data segment and return their label.

544 L<Rs|/Rs> - Layout bytes in read only memory and return their label.

545 L<Rutf8|/Rutf8> - Layout a utf8 encoded string as bytes in read only memory and return their label.

546 L<Rw|/Rw> - Layout words in the data segment and return their label.

547 L<SaveFirstFour|/SaveFirstFour> - Save the first 4 parameter registers making any parameter registers read only.

548 L<SaveFirstSeven|/SaveFirstSeven> - Save the first 7 parameter registers.

549 L<SaveRegIntoMm|/SaveRegIntoMm> - Save the specified register into the numbered zmm at the quad offset specified as a constant number.

550 L<SetLabel|/SetLabel> - Create (if necessary) and set a label in the code section returning the label so set.

551 L<SetMaskRegister|/SetMaskRegister> - Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere.

552 L<SetZF|/SetZF> - Set the zero flag.

553 L<sortHashKeysByIntegerValues|/sortHashKeysByIntegerValues> - Sort the keys of a hash whose values are integers by those values returning the keys so sorted as an array.

554 L<Start|/Start> - Initialize the assembler.

555 L<StatSize|/StatSize> - Stat a file whose name is addressed by rax to get its size in rax.

556 L<StringLength|/StringLength> - Length of a zero terminated string.

557 L<Subroutine|/Subroutine> - Create a subroutine that can be called in assembler code.

558 L<SubroutineStartStack|/SubroutineStartStack> - Initialize a new stack frame.

559 L<testParseUnisyn|/testParseUnisyn> - Test the parse of a unisyn expression.

560 L<Then|/Then> - Then block for an If statement.

561 L<totalBytesAssembled|/totalBytesAssembled> - Total size in bytes of all files assembled during testing.

562 L<ToZero|/ToZero> - Iterate a block the number of times specified in the register which is decremented to zero.

563 L<unlinkFile|/unlinkFile> - Unlink the named file.

564 L<uptoNTimes|/uptoNTimes> - Execute a block of code up to a constant number of times controlled by the named register.

565 L<V|/V> - Define a variable.

566 L<Variable|/Variable> - Create a new variable with the specified name initialized via an optional expression.

567 L<WaitPid|/WaitPid> - Wait for the pid in rax to complete.

568 L<wFromX|/wFromX> - Get the word from the numbered xmm register and return it in a variable.

569 L<wFromZ|/wFromZ> - Get the word from the numbered zmm register and return it in a variable.

570 L<wRegFromZmm|/wRegFromZmm> - Load the specified register from the word at the specified offset located in the numbered zmm.

571 L<wRegIntoZmm|/wRegIntoZmm> - Put the specified register into the word in the numbered zmm at the specified offset in the zmm.

572 L<xmm|/xmm> - Add xmm to the front of a list of register expressions.

573 L<ymm|/ymm> - Add ymm to the front of a list of register expressions.

574 L<zmm|/zmm> - Add zmm to the front of a list of register expressions.

575 L<zmmM|/zmmM> - Add zmm to the front of a register number and a mask after it.

576 L<zmmMZ|/zmmMZ> - Add zmm to the front of a register number and mask and zero after it.

=head1 Installation

This module is written in 100% Pure Perl and, thus, it is easy to read,
comprehend, use, modify and install via B<cpan>:

  sudo cpan install Nasm::X86

=head1 Author

L<philiprbrenan@gmail.com|mailto:philiprbrenan@gmail.com>

L<http://www.appaapps.com|http://www.appaapps.com>

=head1 Copyright

Copyright (c) 2016-2021 Philip R Brenan.

This module is free software. It may be used, redistributed and/or modified
under the same terms as Perl itself.

=cut



# Tests and documentation

sub test
 {my $p = __PACKAGE__;
  binmode($_, ":utf8") for *STDOUT, *STDERR;
  return if eval "eof(${p}::DATA)";
  my $s = eval "join('', <${p}::DATA>)";
  $@ and die $@;
  eval $s;
  $@ and die $@;
  1
 }

test unless caller;

1;
# podDocumentation
__DATA__
use Time::HiRes qw(time);
use Test::Most;

my %test = map {$_=>1} (@ARGV ? @ARGV : 1..99);                                 # Blocks of tests to execute
#say STDERR "Tests: ", dump(\%block);
unlink my $resultFile = "zzzStatus.txt";                                        # File holding consolidated results of tests

bail_on_fail unless onGitHub;

my $localTest = ((caller(1))[0]//'Nasm::X86') eq "Nasm::X86";                   # Local testing mode

Test::More->builder->output("/dev/null") if $localTest;                         # Reduce number of confirmation messages during testing

if ($^O =~ m(bsd|linux|cygwin)i)                                                # Supported systems
 {if (confirmHasCommandLineCommand(q(nasm)) and LocateIntelEmulator){}          # Network assembler and Intel Software Development emulator
  else
   {plan skip_all => qq(Nasm or Intel 64 emulator not available);
   }
 }
else
 {plan skip_all => qq(Not supported on: $^O);
 }

my $start = time;                                                               # Tests

eval {goto latest} unless onGitHub;                                             # Latest test if visible and testing locally

goto test2 unless $test{1};

#latest:
if (1) {                                                                        #TPrintOutStringNL #TPrintErrStringNL #TAssemble
  PrintOutStringNL "Hello World";
  PrintOutStringNL "Hello\nWorld";
  PrintErrStringNL "Hello World";

  ok Assemble eq => <<END, avx512=>0, label=>'t1';
Hello World
Hello
World
END
 }

#latest:
if (1) {                                                                        #TStart #TExit
  Start;
  Exit(0);
  ok Assemble eq => <<END;
END
 }

#latest:
if (1) {                                                                        #TPrintOutRaxInHexNL #TVmovdqu64
  my $s = Rb(0..255);

  Vmovdqu64 xmm1, "[$s]";
  PrintOutRegisterInHex xmm1;
  PrintOutRegisterInHex xmm1;

  Vmovdqu64 ymm1, "[$s]";
  PrintOutRegisterInHex ymm1;
  PrintOutRegisterInHex ymm1;

  Vmovdqu64 zmm1, "[$s]";
  PrintOutRegisterInHex zmm1;
  PrintOutRegisterInHex zmm1;

  ok Assemble eq =><<END;
  xmm1: .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  xmm1: .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  ymm1: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  ymm1: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  zmm1: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  zmm1: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
END
 }

#latest:;
if (1)
 {Mov rax, 1;
  Push rax;
  Mov rax, rsp;
  Vpaddq zmm31, zmm31, "[rax]{1to8}";
  Vpaddd zmm30, zmm30, "[rax]{1to16}";
  Mov rax, 2;
  Vpbroadcastq zmm29, rax;
  PrintOutRegisterInHex 29..31;
  ok Assemble eq=><<END, avx512=>1;
 zmm29: .... .... .... ...2  .... .... .... ...2 - .... .... .... ...2  .... .... .... ...2 + .... .... .... ...2  .... .... .... ...2 - .... .... .... ...2  .... .... .... ...2
 zmm30: .... ...1 .... ...1  .... ...1 .... ...1 - .... ...1 .... ...1  .... ...1 .... ...1 + .... ...1 .... ...1  .... ...1 .... ...1 - .... ...1 .... ...1  .... ...1 .... ...1
 zmm31: .... .... .... ...1  .... .... .... ...1 - .... .... .... ...1  .... .... .... ...1 + .... .... .... ...1  .... .... .... ...1 - .... .... .... ...1  .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #TVpgatherqq
  Mov rax, 0xCC;
  Kmovq k1, rax;
  my ($s, $l) = constantString("1234567"x8);
  $s->setReg(rax);
  Vpgatherqq zmmM(1, 1), "[rax+zmm0]";                                          # Target register must be different from source register
  PrintOutRegisterInHex zmm1, k1;
  ok Assemble eq => <<END, avx512=>1;
  zmm1: 3137 3635 3433 3231  3137 3635 3433 3231 - .... .... .... ...0  .... .... .... ...0 + 3137 3635 3433 3231  3137 3635 3433 3231 - .... .... .... ...0  .... .... .... ...0
    k1: .... .... .... ...0
END
 }

#latest:;
if (1) {                                                                        #TMov #TComment #TRs #TPrintOutMemory #TExit
  Comment "Print a string from memory";
  my $s = "Hello World";
  Mov rax, Rs($s);
  Mov rdi, length $s;
  PrintOutMemory;
  Exit(0);

  ok Assemble(avx512=>0) =~ m(Hello World);
 }

#latest:;
if (1) {                                                                        #TPrintOutMemoryNL #TStringLength
  my $s = Rs("Hello World\n\nHello Skye");
  my $l = StringLength(my $t = V string => $s);
  $t->setReg(rax);
  $l->setReg(rdi);
  PrintOutMemoryNL;

  ok Assemble eq => <<END, avx512=>0;
Hello World

Hello Skye
END
 }

#latest:;
if (1) {                                                                        #TPrintOutRaxInHex #TPrintOutNL #TPrintOutString
  Mov rax, 0x666;
  PrintOutRightInDec rax,  8;
  PrintOutNL;

  ok Assemble avx512=>0, eq=><<END;
    1638
END
 }

#latest:;
if (1) {                                                                        #TPrintOutRaxInHex #TPrintOutNL #TPrintOutString
  my $q = Rs('abababab');
  Mov(rax, "[$q]");
  PrintOutString "rax: ";
  PrintOutRaxInHex;
  PrintOutNL;
  Xor rax, rax;
  PrintOutString "rax: ";
  PrintOutRaxInHex;
  PrintOutNL;

  ok Assemble avx512=>0, eq=><<END;
rax: 6261 6261 6261 6261
rax: .... .... .... ...0
END
 }

#latest:;
if (0) {                                                                        #TPrintOutRegistersInHex #TRs
  my $q = Rs('abababab');
  Mov r10, 0x10;
  Mov r11, 0x11;
  Mov r12, 0x12;
  Mov r13, 0x13;
  Mov r14, 0x14;
  Mov r15, 0x15;
  Mov  r8, 0x08;
  Mov  r9, 0x09;
  Mov rax, 1;
  Mov rbx, 2;
  Mov rcx, 3;
  Mov rdi, 4;
  Mov rdx, 5;
  Mov rsi, 6;
  PrintOutRegistersInHex;

  my $r = Assemble avx512=>0, eq=><<END;
rfl: .... .... .... .2.2
r10: .... .... .... ..10
r11: .... .... .... .2.6
r12: .... .... .... ..12
r13: .... .... .... ..13
r14: .... .... .... ..14
r15: .... .... .... ..15
 r8: .... .... .... ...8
 r9: .... .... .... ...9
rax: .... .... .... ...1
rbx: .... .... .... ...2
rcx: .... .... ..40 197F
rdi: .... .... .... ...4
rdx: .... .... .... ...5
rsi: .... .... .... ...6
END
 }

#latest:;
if (1) {                                                                        #TDs TRs
  my $q = Rs('a'..'z');
  Mov rax, Ds('0'x64);                                                          # Output area
  Vmovdqu32(xmm0, "[$q]");                                                      # Load
  Vprolq   (xmm0,   xmm0, 32);                                                  # Rotate double words in quad words
  Vmovdqu32("[rax]", xmm0);                                                     # Save
  Mov rdi, 16;
  PrintOutMemoryNL;

  ok Assemble eq=><<END;
efghabcdmnopijkl
END
 }

#latest:;
if (1) {
  my $q = Rs(('a'..'p')x2);
  Mov rax, Ds('0'x64);
  Vmovdqu32(ymm0, "[$q]");
  Vprolq   (ymm0,   ymm0, 32);
  Vmovdqu32("[rax]", ymm0);
  Mov rdi, 32;
  PrintOutMemoryNL;

  ok Assemble eq=><<END;
efghabcdmnopijklefghabcdmnopijkl
END
 }

#latest:;
if (1) {
  my $q = Rs my $s = join '', ('a'..'p')x4;                                     # Sample string
  Mov rax, Ds('0'x128);

  Vmovdqu64 zmm0, "[$q]";                                                       # Load zmm0 with sample string
  Vprolq    zmm1, zmm0, 32;                                                     # Rotate left 32 bits in lanes
  Vmovdqu64 "[rax]", zmm1;                                                      # Save results

  Mov rdi, length $s;                                                           # Print results
  PrintOutMemoryNL;

  is_deeply "$s\n", <<END;                                                      # Initial string
abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop
END

  ok Assemble eq => <<END;
efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl
END
 }

#latest:
if (1) {
  my $q = Rb(0..255);
  Vmovdqu8 xmm0, "[$q]";
  Vmovdqu8 xmm1, "[$q+16]";
  Vmovdqu8 xmm2, "[$q+32]";
  Vmovdqu8 xmm3, "[$q+48]";
  PrintOutRegisterInHex xmm0, xmm1,  xmm2, xmm3;

  Vmovdqu8 ymm0, "[$q]";
  Vmovdqu8 ymm1, "[$q+16]";
  Vmovdqu8 ymm2, "[$q+32]";
  Vmovdqu8 ymm3, "[$q+48]";
  PrintOutRegisterInHex ymm0, ymm1, ymm2, ymm3;

  Vmovdqu8 zmm0, "[$q]";
  Vmovdqu8 zmm1, "[$q+16]";
  Vmovdqu8 zmm2, "[$q+32]";
  Vmovdqu8 zmm3, "[$q+48]";
  PrintOutRegisterInHex zmm0, zmm1, zmm2, zmm3;

  ok Assemble( eq=><<END);
  xmm0: .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  xmm1: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
  xmm2: 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
  xmm3: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130
  ymm0: 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  ymm1: 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
  ymm2: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
  ymm3: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130
  zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  zmm1: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
  zmm2: 5F5E 5D5C 5B5A 5958  5756 5554 5352 5150 - 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 + 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
  zmm3: 6F6E 6D6C 6B6A 6968  6766 6564 6362 6160 - 5F5E 5D5C 5B5A 5958  5756 5554 5352 5150 + 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::copyZF #TNasm::X86::Variable::copyZFInverted
  Mov r15, 1;
  my $z = V(zf => undef);
  Cmp r15, 1; $z->copyZF;         $z->outNL;
  Cmp r15, 2; $z->copyZF;         $z->outNL;
  Cmp r15, 1; $z->copyZFInverted; $z->outNL;
  Cmp r15, 2; $z->copyZFInverted; $z->outNL;

  ok Assemble eq => <<END, avx512=>0;
zf: .... .... .... ...1
zf: .... .... .... ...0
zf: .... .... .... ...0
zf: .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #TPrintOutRightInHexNL
  my $N = K number => 0x12345678;

  for my $i(reverse 1..16)
   {PrintOutRightInHexNL $N, $i;
   }
  ok Assemble eq => <<END;
        12345678
       12345678
      12345678
     12345678
    12345678
   12345678
  12345678
 12345678
12345678
2345678
345678
45678
5678
678
78
8
END
 }

#latest:
if (1) {                                                                        #TPrintOutRightInBinNL
  K(count => 64)->for(sub
   {my ($index, $start, $next, $end) = @_;
    PrintOutRightInBinNL K(number => 0x99), K(max => 64) - $index;
   });
  ok Assemble(avx512=>0, eq => <<END);
                                                        10011001
                                                       10011001
                                                      10011001
                                                     10011001
                                                    10011001
                                                   10011001
                                                  10011001
                                                 10011001
                                                10011001
                                               10011001
                                              10011001
                                             10011001
                                            10011001
                                           10011001
                                          10011001
                                         10011001
                                        10011001
                                       10011001
                                      10011001
                                     10011001
                                    10011001
                                   10011001
                                  10011001
                                 10011001
                                10011001
                               10011001
                              10011001
                             10011001
                            10011001
                           10011001
                          10011001
                         10011001
                        10011001
                       10011001
                      10011001
                     10011001
                    10011001
                   10011001
                  10011001
                 10011001
                10011001
               10011001
              10011001
             10011001
            10011001
           10011001
          10011001
         10011001
        10011001
       10011001
      10011001
     10011001
    10011001
   10011001
  10011001
 10011001
10011001
0011001
011001
11001
1001
001
01
1
END
 }

#latest:;
if (1)                                                                          #TOpenWrite #TPrintMemory #TCloseFile
 {my $s = "zzzCreated.data";
  my $f = Rs $s;
  Mov rax, $f;
  OpenWrite;
  Mov r15, rax;
  Mov rax, $f;
  Mov rdi, length $s;
  PrintMemory r15;
  CloseFile;
  ok Assemble eq=><<END, avx512=>1;
END
  ok -e $s;
  unlink $s;
 }

#latest:
if (1) {                                                                        #TAllocateMemory #TFreeMemory
  my $N = K size => 2048;
  my $q = Rs('a'..'p');
  my $address = AllocateMemory $N;

  Vmovdqu8 xmm0, "[$q]";
  $address->setReg(rax);
  Vmovdqu8 "[rax]", xmm0;
  Mov rdi, 16;
  PrintOutMemory;
  PrintOutNL;

  FreeMemory $address, $N;

  ok Assemble eq => <<END;
abcdefghijklmnop
END
 }

#latest:
if (1) {                                                                        #TconstantString
  my ($t, $l) = constantString("Hello World");
  $t->printOutMemoryNL($l);

  ok Assemble eq => <<END, avx512=>1;
Hello World
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Variable::at
 {my $v = V var => 2;
  Mov rax, $v->at;
  PrintOutRegisterInHex rax;
  ok Assemble eq=><<END, avx512=>1;
   rax: .... .... .... ...2
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::allocateMemory #TNasm::X86::Variable::freeMemory
  my $N = K size => 2048;
  my $q = Rs('a'..'p');
  my $address = $N->allocateMemory;

  Vmovdqu8 xmm0, "[$q]";
  $address->setReg(rax);
  Vmovdqu8 "[rax]", xmm0;
  Mov rdi, 16;
  PrintOutMemory;
  PrintOutNL;

  $address->freeMemory($N);

  ok Assemble eq => <<END;
abcdefghijklmnop
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::outNL
  my $a = V a => 0x1111;
  $a->outNL('');
  $a->outRightInBinNL;
  $a->outRightInDecNL;
  $a->outRightInHexNL;
  ok Assemble eq => <<END;
.... .... .... 1111
   1000100010001
            4369

END
 }

#latest:
if (1) {                                                                        #TReadTimeStampCounter
  for(1..10)
   {ReadTimeStampCounter;
    PrintOutRegisterInHex rax;
   }

  my @s = split /\n/, Assemble(avx512=>0);
  my @S = sort @s;
  is_deeply \@s, \@S;
 }

#latest:
if (1) {                                                                        #TIf #TEf #TThen  #TElse
  my $n0 = K(zero => 0);
  If $n0 == 0,
  Then
   {PrintOutStringNL "zero == 0";
   },
  Ef {$n0 == 1}
  Then
   {PrintOutStringNL "zero == 1";
   },
  Else
   {PrintOutStringNL "zero == 2";
   };

  my $n1 = K(one => 1);
  If $n1 == 0,
  Then
   {PrintOutStringNL "one == 0";
   },
  Ef {$n1 == 1}
  Then
   {PrintOutStringNL "one == 1";
   },
  Else
   {PrintOutStringNL "one == 2";
   };

  my $n2 = K(two => 2);
  If $n2 == 0,
  Then
   {PrintOutStringNL "two == 0";
   },
  Ef {$n2 == 1}
  Then
   {PrintOutStringNL "two == 1";
   },
  Else
   {PrintOutStringNL "two == 2";
   };

  ok Assemble eq => <<END, avx512=>0;
zero == 0
one == 1
two == 2
END
 }

if (1) {                                                                        #TIfNz
  Mov rax, 0;
  Test rax,rax;
  IfNz
  Then
   {PrintOutRegisterInHex rax;
   },
  Else
   {PrintOutRegisterInHex rbx;
   };
  Mov rax, 1;
  Test rax,rax;
  IfNz
  Then
   {PrintOutRegisterInHex rcx;
   },
  Else
   {PrintOutRegisterInHex rdx;
   };

  ok Assemble(avx512=>0) =~ m(rbx.*rcx)s;
 }

if (1) {                                                                        #TFork #TGetPid #TGetPPid #TWaitPid
  Fork;                                                                         # Fork

  Test rax,rax;
  IfNz                                                                          # Parent
  Then
   {Mov rbx, rax;
    WaitPid;
    GetPid;                                                                     # Pid of parent as seen in parent
    Mov rcx,rax;
    PrintOutRegisterInHex rax, rbx, rcx;
   },
  Else                                                                          # Child
   {Mov r8,rax;
    GetPid;                                                                     # Child pid as seen in child
    Mov r9,rax;
    GetPPid;                                                                    # Parent pid as seen in child
    Mov r10,rax;
    PrintOutRegisterInHex r8, r9, r10;
   };

  my $r = Assemble(avx512=>0);

#    r8: 0000 0000 0000 0000   #1 Return from fork as seen by child
#    r9: 0000 0000 0003 0C63   #2 Pid of child
#   r10: 0000 0000 0003 0C60   #3 Pid of parent from child
#   rax: 0000 0000 0003 0C63   #4 Return from fork as seen by parent
#   rbx: 0000 0000 0003 0C63   #5 Wait for child pid result
#   rcx: 0000 0000 0003 0C60   #6 Pid of parent

  if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
   {ok $2 eq $4;
    ok $2 eq $5;
    ok $3 eq $6;
    ok $2 gt $6;
   }
 }

if (!onGitHub) {                                                                #TGetUid
  GetUid;                                                                       # Userid
  PrintOutRegisterInHex rax;

  my $r = Assemble(avx512=>0);
  ok $r =~ m(3E8);
 }

#latest:;
if (1) {                                                                        #TStatSize
  Mov rax, Rs $0;                                                               # File to stat
  StatSize;                                                                     # Stat the file
  PrintOutRegisterInHex rax;

  my $r = Assemble(avx512=>0) =~ s( ) ()gsr;
  if ($r =~ m(rax:([0-9a-f]{16}))is)                                            # Compare file size obtained with that from fileSize()
   {is_deeply $1, sprintf("%016X", fileSize($0));
   }
 }

#latest:;
if (1) {                                                                        #TOpenRead #TCloseFile #TOpenWrite
  Mov rax, Rs($0);                                                              # File to read
  OpenRead;                                                                     # Open file
  PrintOutRegisterInHex rax;
  CloseFile;                                                                    # Close file
  PrintOutRegisterInHex rax;

  Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write
  OpenWrite;                                                                    # Open file
  CloseFile;                                                                    # Close file

  ok Assemble eq => <<END;
   rax: .... .... .... ...3
   rax: .... .... .... ...0
END
  ok -e $f;                                                                     # Created file
  unlink $f;
 }

if (1) {                                                                        #TFor
  For
   {my ($start, $end, $next) = @_;
    Cmp rax, 3;
    Jge $end;
    PrintOutRegisterInHex rax;
   } rax, 16, 1;

  ok Assemble eq => <<END, avx512=>0;
   rax: .... .... .... ...0
   rax: .... .... .... ...1
   rax: .... .... .... ...2
END
 }

if (1) {                                                                        #TAndBlock #TFail
  Mov rax, 1; Mov rdx, 2;
  AndBlock
   {my ($fail, $end, $start) = @_;
    Cmp rax, 1;
    Jne $fail;
    Cmp rdx, 2;
    Jne $fail;
    PrintOutStringNL "Pass";
   }
  Fail
   {my ($end, $fail, $start) = @_;
    PrintOutStringNL "Fail";
   };

  ok Assemble eq => <<END, avx512=>0;
Pass
END
 }

if (1) {                                                                        #TOrBlock #TPass
  Mov rax, 1;
  OrBlock
   {my ($pass, $end, $start) = @_;
    Cmp rax, 1;
    Je  $pass;
    Cmp rax, 2;
    Je  $pass;
    PrintOutStringNL "Fail";
   }
  Pass
   {my ($end, $pass, $start) = @_;
    PrintOutStringNL "Pass";
   };

  ok Assemble eq => <<END, avx512=>0;
Pass
END
 }

#latest:;
if (1) {                                                                        #TToZero
  Mov rax, 3;
  ToZero
   {PrintOutStringNL "AAAA";
   }  rax;
  ok Assemble eq => <<END, clocks=>4374;
AAAA
AAAA
AAAA
END
 }

if (1) {                                                                        #TPrintOutRaxInReverseInHex #TPrintOutMemoryInHex
  Mov rax, 0x07654321;
  Shl rax, 32;
  Or  rax, 0x07654321;
  PushR rax;

  PrintOutRaxInHex;
  PrintOutNL;
  PrintOutRaxInReverseInHex;
  PrintOutNL;

  Mov rax, rsp;
  Mov rdi, 8;
  PrintOutMemoryInHex;
  PrintOutNL;
  PopR rax;

  Mov rax, 4096;
  PushR rax;
  Mov rax, rsp;
  Mov rdi, 8;
  PrintOutMemoryInHex;
  PrintOutNL;
  PopR rax;

  ok Assemble eq => <<END, avx512=>0;
.765 4321 .765 4321
2143 65.7 2143 65.7
2143 65.7 2143 65.7
..10 .... .... ....
END
 }

if (1) {                                                                        #TPushR #TPopR
  Mov rax, 0x11111111;
  Mov rbx, 0x22222222;
  PushR my @save = (rax, rbx);
  Mov rax, 0x33333333;
  PopR;
  PrintOutRegisterInHex rax;
  PrintOutRegisterInHex rbx;

  ok Assemble eq => <<END, avx512=>0;
   rax: .... .... 1111 1111
   rbx: .... .... 2222 2222
END
 }

#latest:
if (1) {                                                                        #TPushR #TPopR
  LoadZmm(17, 0x10..0x50);
  PrintOutRegisterInHex zmm17;
  Mov r14, 2; Mov r15, 3;
  PrintOutRegisterInHex r14, r15;
  PushR 14, 15, 16..31;
  LoadZmm(17, 0x20..0x70);
  PrintOutRegisterInHex zmm17;
  Mov r14, 22; Mov r15, 33;
  PopR;
  PrintOutRegisterInHex zmm17;
  PrintOutRegisterInHex r14, r15;
  ok Assemble eq => <<END, avx512=>1;
 zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
   r14: .... .... .... ...2
   r15: .... .... .... ...3
 zmm17: 5F5E 5D5C 5B5A 5958  5756 5554 5352 5150 - 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 + 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120
 zmm17: 4F4E 4D4C 4B4A 4948  4746 4544 4342 4140 - 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 + 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 - 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110
   r14: .... .... .... ...2
   r15: .... .... .... ...3
END
 }

#latest:;
if (1) {                                                                        #TClearMemory #TPrintOutMemory_InHexNL #TPrintOutMemory_InHex
  K(loop => 8+1)->for(sub
   {my ($index, $start, $next, $end) = @_;
    $index->setReg(15);
    Push r15;
   });

  Mov rax, rsp;
  Mov rdi, 8*9;
  PrintOutMemory_InHexNL;
  ClearMemory(V(address => rax), K(size => 8*9));
  PrintOutMemory_InHex;
  PrintOutNL;

  ok Assemble eq => <<END;
.8__ ____ ____ ____  .7__ ____ ____ ____  .6__ ____ ____ ____  .5__ ____ ____ ____  .4__ ____ ____ ____  .3__ ____ ____ ____  .2__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____
____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:;
if (1) {                                                                        #TPrintOutOneRegisterInHex #TPrintOutOneRegisterInHexNL
  Mov rax, 0x22;
  Mov rbx, 0x33;
  PrintOutOneRegisterInHex   rax;
  PrintOutOneRegisterInHexNL rbx;

  ok Assemble eq => <<END;
.... .... .... ..22.... .... .... ..33
END
 }

#latest:;
if (1) {                                                                        #TPrintOutRaxInDec #TPrintOutRaxInDecNL #TPrintOutRax_InHex #TPrintOutRax_InHexNL #TPrintOutRegisterInHex #TPrintOutRightInBin #TPrintOutRightInDec #TPrintOutRightInHex #TPrintOutSpace
  Mov rax, 42;
  Mov rbx, 21;
  PrintOutRaxInDec;
  PrintOutSpace;
  PrintOutRaxInDecNL;

  PrintOutRax_InHex;
  PrintOutSpace;
  PrintOutRax_InHexNL;

  PrintOutRegisterInHex rbx;
  PrintOutNL;

  PrintOutRightInBin K(key => 17), K width => 16;
  PrintOutSpace;
  PrintOutRightInDec K(key => 17), K width => 2;
  PrintOutSpace;
  PrintOutRightInHex K(key => 17), K width => 2;
  PrintOutNL;

  ok Assemble eq => <<END;
42 42
____ ____ ____ __2A ____ ____ ____ __2A
   rbx: .... .... .... ..15

           10001 17 11
END
 }

#latest:;
if (1) {                                                                        #TPrintOutRaxAsText #TPrintOutRaxAsTextNL
  Mov rax, 0x636261;
  PrintOutRaxAsText;
  Mov rax, 0x64636261;
  PrintOutRaxAsTextNL;

  ok Assemble eq => <<END;
abcabcd
END
 }

#latest:;
if (1) {                                                                        #TPrintOutOneRegisterInHex #TPrintOutOneRegisterInHexNL
  Mov rax, 0x61;
  PrintOutRaxAsChar;
  Mov rax, 0x62;
  PrintOutRaxAsCharNL;

  ok Assemble eq => <<END;
ab
END
 }

#latest:;
if (1) {                                                                        #TAllocateMemory #TFreeMemory #TClearMemory
  my $N = K size => 4096;                                                       # Size of the initial allocation which should be one or more pages

  my $A = AllocateMemory $N;

  ClearMemory($A, $N);

  $A->setReg(rax);
  Mov rdi, 128;
  PrintOutMemory_InHexNL;

  FreeMemory $A, $N;

  ok Assemble eq => <<END;
____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:;
if (1) {
  Mov rax, 0x44332211;
  PrintOutRegisterInHex rax;

  my $s = Subroutine
   {PrintOutRegisterInHex rax;
    Inc rax;
    PrintOutRegisterInHex rax;
   } name => "printIncPrint";

  $s->call;

  PrintOutRegisterInHex rax;

  my $r = Assemble(avx512=>0, eq=><<END);
   rax: .... .... 4433 2211
   rax: .... .... 4433 2211
   rax: .... .... 4433 2212
   rax: .... .... 4433 2212
END
 }

#latest:;
if (!!onGitHub) {                                                               #TReadFile #TPrintMemory
  my $file = V(file => Rs $0);
  my ($address, $size) = ReadFile $file;                                        # Read file into memory
  $address->setReg(rax);                                                        # Address of file in memory
  $size   ->setReg(rdi);                                                        # Length  of file in memory
  PrintOutMemory;                                                               # Print contents of memory to stdout

  my $r = Assemble(avx512=>0);                                                  # Assemble and execute
  ok stringMd5Sum($r) eq fileMd5Sum($0);                                        # Output contains this file
 }

#latest:;
if (1) {                                                                        #TCreateArea #TNasm::X86::Area::out #TNasm::X86::Area::outNL #TNasm::X86::Area::q #TNasm::X86::Area::ql
  my $a = CreateArea;
  $a->q('aa');
  $a->outNL;
  $a->ql('bb');
  $a->out;
  ok Assemble eq => <<END, avx512=>0;
aa
aabb
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;

  $T->dump("AA");
  $T->put(K(key=>0), $t);
  $T->dump("BB");

  ok Assemble eq => <<END, avx512=>1;
AA
- empty
BB
At:   C0                    length:    1,  data:  100,  nodes:  140,  first:   80, root, leaf,  trees:             1
  Index:    0
  Keys :    0
  Data :   0*
- empty
end
END
}

#latest:
if (1) {                                                                        #TNasm::X86::Area::print
  my $a = CreateArea;
  my $z = $a->allocZmmBlock;
  LoadZmm 31, (ord('a')) x 64;
  $a->putZmmBlock($z, 31);
  $a->printOut($z, K length => 3);

  ok Assemble eq => <<END, avx512=>1;
aaa
END
 }

#latest:
if (1) {                                                                        #TArea::dump
  my $a = CreateArea;
  my $b = CreateArea;
  $a->q("aaaa");
  $a->dump("aaaaa");
  $b->q("bbbb");
  $b->dump("bbbb");

  ok Assemble eq => <<END, avx512=>0;
aaaaa
Area     Size:     4096    Used:       68
.... .... .... ...0 | __10 ____ ____ ____  44__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
bbbb
Area     Size:     4096    Used:       68
.... .... .... ...0 | __10 ____ ____ ____  44__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6262 6262 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

if (1) {                                                                        #TCreateArea #TArea::clear #TArea::out #TArea::copy #TArea::nl
  my $a = CreateArea;
  my $b = CreateArea;
  $a->q('aa');
  $b->q('bb');
  $a->out;
  PrintOutNL;
  $b->out;
  PrintOutNL;
  ok Assemble eq => <<END, avx512=>0;
aa
bb
END
 }

if (1) {                                                                        #TCreateArea #TArea::clear #TArea::out #TArea::copy #TArea::nl
  my $a = CreateArea;
  my $b = CreateArea;
  $a->q('aa');
  $a->q('AA');
  $a->out;
  PrintOutNL;
  ok Assemble eq => <<END, avx512=>0;
aaAA
END
 }

if (1) {                                                                        #TCreateArea #TArea::clear #TArea::out #TArea::copy #TArea::nl
  my $a = CreateArea;
  my $b = CreateArea;
  $a->q('aa');
  $b->q('bb');
  $a->q('AA');
  $b->q('BB');
  $a->q('aa');
  $b->q('bb');
  $a->out;
  $b->out;
  PrintOutNL;
  ok Assemble eq => <<END, avx512=>0;
aaAAaabbBBbb
END
 }

#latest:
if (1) {                                                                        #TCreateArea #TNasm::X86::Area::clear #TNasm::X86::Area::out #TNasm::X86::Area::outNL  #TNasm::X86::Area::append
  my $a = CreateArea;
  $a->q('ab');
  my $b = CreateArea;
  $b->append($a);
  $b->append($a);
  $a->append($b);
  $b->append($a);
  $a->append($b);
  $b->append($a);
  $b->append($a);
  $b->append($a);
  $b->append($a);


  $a->out;   PrintOutNL;
  $b->outNL;
  my $sa = $a->used; $sa->outNL;
  my $sb = $b->used; $sb->outNL;
  $a->clear;
  my $sA = $a->used; $sA->outNL;
  my $sB = $b->used; $sB->outNL;

  ok Assemble eq => <<END, avx512=>0;
abababababababab
ababababababababababababababababababababababababababababababababababababab
area used up: .... .... .... ..10
area used up: .... .... .... ..4A
area used up: .... .... .... ...0
area used up: .... .... .... ..4A
END
 }


if (1) {                                                                        # Mask register instructions #TClearRegisters
  Mov rax,1;
  Kmovq k0,  rax;
  Kaddb k0,  k0, k0;
  Kaddb k0,  k0, k0;
  Kaddb k0,  k0, k0;
  Kmovq rax, k0;
  PushR k0;
  ClearRegisters k0;
  Kmovq k1, k0;
  PopR  k0;
  PrintOutRegisterInHex k0;
  PrintOutRegisterInHex k1;

  ok Assemble( eq => <<END)
    k0: .... .... .... ...8
    k1: .... .... .... ...0
END
 }

if (1) {                                                                        # Count leading zeros
  Mov   rax, 8;                                                                 # Append a constant to the area
  Lzcnt rax, rax;                                                               # New line
  PrintOutRegisterInHex rax;

  Mov   rax, 8;                                                                 # Append a constant to the area
  Tzcnt rax, rax;                                                               # New line
  PrintOutRegisterInHex rax;

  ok Assemble( eq => <<END)
   rax: .... .... .... ..3C
   rax: .... .... .... ...3
END
 }

#latest:;
if (1) {                                                                        #TArea::nl
  my $s = CreateArea;
  $s->q("A");
  $s->nl;
  $s->q("B");
  $s->out;
  PrintOutNL;

  ok Assemble eq => <<END, avx512=>0;
A
B
END
 }

#latest:;
if (!!onGitHub) {                                                               # Print this file - slow -  #TArea::read #TArea::z #TArea::q
  my $s = CreateArea;                                                           # Create a string
  $s->read(K file => Rs($0));
  $s->out;

  my $r = Assemble;
  is_deeply stringMd5Sum($r), fileMd5Sum($0);                                   # Output contains this file
 }

#latest:;
if (1) {                                                                        # Print rdi in hex into an area #TGetPidInHex
  GetPidInHex;
  Mov r15, rax;

  GetPidInHex;
  Cmp r15, rax;
  IfEq Then {PrintOutStringNL "Same"}, Else {PrintOutStringNL "Diff"};

  ok Assemble eq => <<END, avx512=>0;
Same
END
 }

#latest:;
if (0 and !onGitHub) {                                                          # Execute the content of an area #TexecuteFileViaBash #TArea::write #TArea::out #TunlinkFile #TArea::ql
  my $s = CreateArea;                                                           # Create a string
  $s->ql(<<END);                                                                # Write code to execute
#!/usr/bin/bash
whoami
END
  $s->write         (my $f = V('file', Rs("zzz.sh")));                          # Write code to a file
  executeFileViaBash($f);                                                       # Execute the file
  unlinkFile        ($f);                                                       # Delete the file

  my $u = qx(whoami); chomp($u);
  ok Assemble eq => <<END, avx512=>0;
phil
END
 }

#latest:;
if (!hasAvx512) {                                                               # Make an area readonly
  my $s = CreateArea;                                                           # Create an area
  $s->q("Hello");                                                               # Write code to area
  $s->makeReadOnly;                                                             # Make area read only
  $s->q(" World");                                                              # Try to write to area

  ok Assemble(debug=>2, ptr=>1) =~ m(SDE ERROR: DEREFERENCING BAD MEMORY POINTER.*mov byte ptr .rax.rdx.1., r8b);
 }

#latest:;
if (1) {                                                                        # Make a read only area writable  #TNasm::X86::Area::makeWriteable #TNasm::X86::Area::makeReadOnly
  my $s = CreateArea;                                                           # Create an area
  $s->q("Hello");                                                               # Write data to area
  $s->makeReadOnly;                                                             # Make area read only - tested above
  $s->makeWriteable;                                                            # Make area writable again
  $s->q(" World");                                                              # Try to write to area
  $s->outNL;

  ok Assemble(avx512=>0, eq => <<END);
Hello World
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::allocate
  my $s = CreateArea;
  my $o1 = $s->allocate(0x20);
  my $o2 = $s->allocate(0x30);
  my $o3 = $s->allocate(0x10);
  $o1->outNL;
  $o2->outNL;
  $o3->outNL;

  ok Assemble eq => <<END, avx512=>0;
offset: .... .... .... ..40
offset: .... .... .... ..60
offset: .... .... .... ..90
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::checkYggdrasilCreated #TNasm::X86::Area::yggdrasil
  my $A = CreateArea;
  my $t = $A->checkYggdrasilCreated;
     $t->found->outNL;
  my $y = $A->yggdrasil;
  my $T = $A->checkYggdrasilCreated;
     $T->found->outNL;
  ok Assemble debug => 0, eq => <<END, avx512=>1;
found  : .... .... .... ...0
found  : .... .... .... ...1
END
 }

# It is one of the happiest characteristics of this glorious country that official utterances are invariably regarded as unanswerable

#latest:;
if (1) {                                                                        #TPrintOutZF #TSetZF #TClearZF #TIfC #TIfNc #TIfZ #IfNz
  SetZF;
  PrintOutZF;
  ClearZF;
  PrintOutZF;
  SetZF;
  PrintOutZF;
  SetZF;
  PrintOutZF;
  ClearZF;
  PrintOutZF;

  SetZF;
  IfZ  Then {PrintOutStringNL "Zero"},     Else {PrintOutStringNL "NOT zero"};
  ClearZF;
  IfNz Then {PrintOutStringNL "NOT zero"}, Else {PrintOutStringNL "Zero"};

  Mov r15, 5;
  Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
  Shr r15, 1; IfC  Then {PrintOutStringNL "Carry"}   , Else {PrintOutStringNL "NO carry"};
  Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};
  Shr r15, 1; IfNc Then {PrintOutStringNL "NO carry"}, Else {PrintOutStringNL "Carry"};

  ok Assemble eq => <<END, avx512=>0;
ZF=1
ZF=0
ZF=1
ZF=1
ZF=0
Zero
NOT zero
Carry
NO carry
Carry
NO carry
END
 }

#latest:;
if (1) {                                                                        #TLabel  #TSetLabel #TRegisterSize #TSaveFirstFour #TSaveFirstSeven #TRestoreFirstFour #TRestoreFirstSeven #TRestoreFirstFourExceptRax #TRestoreFirstSevenExceptRax #TRestoreFirstFourExceptRaxAndRdi #TRestoreFirstSevenExceptRaxAndRdi #TReverseBytesInRax
  Mov rax, 1;
  Mov rdi, 1;
  SaveFirstFour;
  Mov rax, 2;
  Mov rdi, 2;
  SaveFirstSeven;
  Mov rax, 3;
  Mov rdi, 4;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstSeven;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFour;
  PrintOutRegisterInHex rax, rdi;

  SaveFirstFour;
  Mov rax, 2;
  Mov rdi, 2;
  SaveFirstSeven;
  Mov rax, 3;
  Mov rdi, 4;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstSevenExceptRax;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRax;
  PrintOutRegisterInHex rax, rdi;

  SaveFirstFour;
  Mov rax, 2;
  Mov rdi, 2;
  SaveFirstSeven;
  Mov rax, 3;
  Mov rdi, 4;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  ok Assemble eq => <<END, avx512=>0;
   rax: .... .... .... ...3
   rdi: .... .... .... ...4
   rax: .... .... .... ...2
   rdi: .... .... .... ...2
   rax: .... .... .... ...1
   rdi: .... .... .... ...1
   rax: .... .... .... ...3
   rdi: .... .... .... ...4
   rax: .... .... .... ...3
   rdi: .... .... .... ...2
   rax: .... .... .... ...3
   rdi: .... .... .... ...1
   rax: .... .... .... ...3
   rdi: .... .... .... ...4
   rax: .3.. .... .... ....
END

  ok 8 == RegisterSize rax;
 }

#latest:
if (1) {                                                                        #TRb #TRd #TRq #TRw #TDb #TDd #TDq #TDw #TCopyMemory
  my $s = Rb 0; Rb 1; Rw 2; Rd 3;  Rq 4;
  my $t = Db 0; Db 1; Dw 2; Dd 3;  Dq 4;

  Vmovdqu8 xmm0, "[$s]";
  Vmovdqu8 xmm1, "[$t]";
  PrintOutRegisterInHex xmm0;
  PrintOutRegisterInHex xmm1;
  Sub rsp, 16;

  Mov rax, rsp;                                                                 # Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi
  Mov rdi, 16;
  Mov rsi, $s;
  CopyMemory(V(source => rsi), V(target => rax), V size => rdi);
  PrintOutMemory_InHexNL;

  ok Assemble eq => <<END;
  xmm0: .... .... .... ...4  .... ...3 ...2 .1..
  xmm1: .... .... .... ...4  .... ...3 ...2 .1..
__.1 .2__ .3__ ____  .4__ ____ ____ ____
END
 }

#latest:
if (1) {
  my ($s, $l) =                                                                 #TCopyMemory64
    constantString('0123456789abcdef'x64);
  my $t = $l->allocateMemory;
  my $N = K blocks => 2;
  CopyMemory64($s, $t, $N);
  $t->printOutMemoryNL($N*16);
  ok Assemble eq => <<END, avx512=>1;
0123456789abcdef0123456789abcdef
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::setReg #TNasm::X86::Variable::getReg #TNasm::X86::Variable::copy #TNasm::X86::Variable::out #TNasm::X86::Variable::outNL
  my $a = V(a => 1);
  my $b = V(b => 2);
  my $c = $a + $b;
  Mov r15, 22;
  $a->getReg(r15);
  $b->copy($a);
  $b = $b + 1;
  $b->setReg(14);
  $a->outNL;
  $b->outNL;
  $c->out;
  PrintOutNL;
  PrintOutRegisterInHex r14, r15;

  ok Assemble eq => <<END, avx512=>0;
a: .... .... .... ..16
(b add 1): .... .... .... ..17
(a add b): .... .... .... ...3
   r14: .... .... .... ..17
   r15: .... .... .... ..16
END
 }

#latest:
if (1) {                                                                        #TV #TK #TG #TNasm::X86::Variable::copy #TNasm::X86::Variable::outSpaces #TNasm::X86::Variable::outRightInHex #TNasm::X86::Variable::outRightInHexNL #TNasm::X86::Variable::outRightInDec #TNasm::X86::Variable::outRightInDecNL #TNasm::X86::Variable::outRightInBin #TNasm::X86::Variable::outRightInBinNL #TNasm::X86::Variable::outInDecNL #TNasm::X86::Variable::outInDec #TNasm::X86::Variable::out
  my $a = K abc => 0x123;
  $a->out;
  PrintOutNL;

  $a->outInDec; PrintOutNL; $a->outInDecNL;
  $a->outRightInBin(16); PrintOutNL; $a->outRightInBinNL(16);
  $a->outRightInDec(16); PrintOutNL; $a->outRightInDecNL;
  $a->outRightInHex(16); PrintOutNL; $a->outRightInHexNL;

  PrintOutString 'a';
  K(key => 2)->outSpaces;
  PrintOutStringNL 'b';

  ok Assemble eq => <<END, avx512=>1;
abc: .... .... .... .123
abc: 291
abc: 291
       100100011
       100100011
             291
             291
             123

a  b
END
 }

#latest:
if (1) {                                                                        #TV #TK #TG #TNasm::X86::Variable::copy
  my $s = Subroutine
   {my ($p) = @_;
    $$p{v}->copy($$p{v} + $$p{k} + $$p{g} + 1);
   } name => 'add', parameters=>[qw(v k g)];

  my $v = V(v => 1);
  my $k = K(k => 2);
  my $g = V(g => 3);
  $s->call(parameters=>{v=>$v, k=>$k, g=>$g});
  $v->outNL;

  ok Assemble eq => <<END, avx512=>0;
v: .... .... .... ...7
END
 }

#latest:
if (1) {                                                                        #TV #TK #TG #TNasm::X86::Variable::copy
  my $g = V g => 0;
  my $s = Subroutine
   {my ($p) = @_;
    $$p{g}->copy(K value => 1);
   } name => 'ref2', parameters=>[qw(g)];

  my $t = Subroutine
   {my ($p) = @_;
    $s->call(parameters=>{g=>$$p{g}});
   } name => 'ref', parameters=>[qw(g)];

  $t->call(parameters=>{g=>$g});
  $g->outNL;

  ok Assemble eq => <<END, avx512=>0;
g: .... .... .... ...1
END
 }

#latest:;
if (1) {                                                                        #TIf #TThen #TElse
  my $a = K(key => 1);
  If $a > 0,
  Then {Mov rax, 1},
  Else {Mov rax, 2};
  PrintOutRegisterInHex rax;

  ok Assemble eq=><<END, avx512=>1;
   rax: .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #TSubroutine
  my $g = V g => 3;
  my $s = Subroutine
   {my ($p, $s, $sub) = @_;
    my $g = $$p{g};
    $g->copy($g - 1);
    $g->outNL;
    If $g > 0,
    Then
     {$sub->call(parameters=>{g => $g});
     };
   } parameters=>[qw(g)], name => 'ref';

  $s->call(parameters=>{g => $g});

  ok Assemble eq => <<END;
g: .... .... .... ...2
g: .... .... .... ...1
g: .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TPrintOutTraceBack
  my $d = V depth => 3;                                                         # Create a variable on the stack

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;                                                     # Parameters, structures, subroutine descriptor
    $$p{depth}->outNL;
    my $d = $$p{depth}->copy($$p{depth} - 1);                                   # Modify the variable referenced by the parameter

    If $d > 0,
    Then
     {$sub->call(parameters => {depth => $d});                                  # Recurse
     };

   } parameters =>[qw(depth)], name => 'ref';

  $s->call(parameters=>{depth => $d});

  $d->outNL;
  ok Assemble eq => <<END, avx512=>0;
depth: .... .... .... ...3
depth: .... .... .... ...2
depth: .... .... .... ...1
depth: .... .... .... ...0
END
 }

#latest:;
if (1) {                                                                        #TAllocateMemory #TPrintOutMemoryInHexNL #TCopyMemory
  my $N = 256;
  my $s = Rb 0..$N-1;
  my $a = AllocateMemory K size => $N;
  CopyMemory(V(source => $s), $a, K(size => $N));

  my $b = AllocateMemory K size => $N;
  CopyMemory($a, $b, K size => $N);

  $b->setReg(rax);
  Mov rdi, $N;
  PrintOutMemory_InHexNL;

  ok Assemble eq => <<END, avx512=>0;
__.1 .2.3 .4.5 .6.7  .8.9 .A.B .C.D .E.F  1011 1213 1415 1617  1819 1A1B 1C1D 1E1F  2021 2223 2425 2627  2829 2A2B 2C2D 2E2F  3031 3233 3435 3637  3839 3A3B 3C3D 3E3F  4041 4243 4445 4647  4849 4A4B 4C4D 4E4F  5051 5253 5455 5657  5859 5A5B 5C5D 5E5F  6061 6263 6465 6667  6869 6A6B 6C6D 6E6F  7071 7273 7475 7677  7879 7A7B 7C7D 7E7F  8081 8283 8485 8687  8889 8A8B 8C8D 8E8F  9091 9293 9495 9697  9899 9A9B 9C9D 9E9F  A0A1 A2A3 A4A5 A6A7  A8A9 AAAB ACAD AEAF  B0B1 B2B3 B4B5 B6B7  B8B9 BABB BCBD BEBF  C0C1 C2C3 C4C5 C6C7  C8C9 CACB CCCD CECF  D0D1 D2D3 D4D5 D6D7  D8D9 DADB DCDD DEDF  E0E1 E2E3 E4E5 E6E7  E8E9 EAEB ECED EEEF  F0F1 F2F3 F4F5 F6F7  F8F9 FAFB FCFD FEFF
END
 }

if (1) {                                                                        # Variable length shift
  Mov rax, -1;
  Mov cl, 30;
  Shl rax, cl;
  Kmovq k0, rax;
  PrintOutRegisterInHex k0;

  ok Assemble( eq=><<END);
    k0: FFFF FFFF C0.. ....
END
 }

#latest:;
if (1) {                                                                        # Expand
  ClearRegisters rax;
  Bts rax, 14;
  Not rax;
  PrintOutRegisterInHex rax;
  Kmovq k1, rax;
  PrintOutRegisterInHex k1;

  Mov rax, 1;
  Vpbroadcastb zmm0, rax;
  PrintOutRegisterInHex zmm0;

  Vpexpandd "zmm1{k1}", zmm0;
  PrintOutRegisterInHex zmm1;

  ok Assemble eq => <<END;
   rax: FFFF FFFF FFFF BFFF
    k1: FFFF FFFF FFFF BFFF
  zmm0: .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1 - .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1 + .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1 - .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1
  zmm1: .1.1 .1.1 .... ....  .1.1 .1.1 .1.1 .1.1 - .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1 + .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1 - .1.1 .1.1 .1.1 .1.1  .1.1 .1.1 .1.1 .1.1
END
 }

#latest:;
if (1) {
  my $P = "2F";                                                                 # Value to test for
  my $l = Rb 0;  Rb $_ for 1..RegisterSize zmm0;                                # The numbers 0..63
  Vmovdqu8 zmm0, "[$l]";                                                        # Load data to test
  PrintOutRegisterInHex zmm0;
  PrintOutRegisterInHex zmm0;

  Mov rax, "0x$P";                                                              # Broadcast the value to be tested
  Vpbroadcastb zmm1, rax;
  PrintOutRegisterInHex zmm1;

  for my $c(0..7)                                                               # Each possible test
   {my $m = "k$c";
    Vpcmpub $m, zmm1, zmm0, $c;
    PrintOutRegisterInHex $m;
   }

  Kmovq rax, k0;                                                                # Count the number of trailing zeros in k0
  Tzcnt rax, rax;

  PrintOutRegisterInHex rax;

  ok Assemble( eq=><<END);
  zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
  zmm1: 2F2F 2F2F 2F2F 2F2F  2F2F 2F2F 2F2F 2F2F - 2F2F 2F2F 2F2F 2F2F  2F2F 2F2F 2F2F 2F2F + 2F2F 2F2F 2F2F 2F2F  2F2F 2F2F 2F2F 2F2F - 2F2F 2F2F 2F2F 2F2F  2F2F 2F2F 2F2F 2F2F
    k0: .... 80.. .... ....
    k1: FFFF .... .... ....
    k2: FFFF 80.. .... ....
    k3: .... .... .... ...0
    k4: FFFF 7FFF FFFF FFFF
    k5: .... FFFF FFFF FFFF
    k6: .... 7FFF FFFF FFFF
    k7: .... .... .... ..-1
   rax: .... .... .... ..2F
END
#   0 eq    1 lt    2 le    4 ne    5 ge    6 gt   comparisons
 }

#latest:;
if (1) {
  my $P = "2F";                                                                 # Value to test for
  my $l = Rb 0;  Rb $_ for 1..RegisterSize zmm0;                                # The numbers 0..63
  Vmovdqu8 zmm0, "[$l]";                                                        # Load data to test

  Mov rax, "0x$P";                                                              # Broadcast the value to be tested
  Vpbroadcastb zmm1, rax;

  for my $c(0..7)                                                               # Each possible test
   {my $m = "k$c";
    Vpcmpub $m, zmm1, zmm0, $c;
   }

  Kmovq rax, k0;                                                                # Count the number of trailing zeros in k0
  Tzcnt rax, rax;
  PrintOutRegisterInHex rax;

  Kmovq rax, k0;                                                                # Count the number of leading zeros in k0
  Lzcnt rax, rax;
  PrintOutRegisterInHex rax;

  ok Assemble( eq=><<END);
   rax: .... .... .... ..2F
   rax: .... .... .... ..10
END
 }

#latest:;
if (1) {                                                                        #TStringLength
  StringLength(V string => Rs("abcd"))->outNL;
  Assemble eq => <<END, avx512=>0;
size: .... .... .... ...4
END
 }

#latest:;
if (0) {                                                                        # Hash a string #THash
  Mov rax, "[rbp+24]";                                                          # Address of string as parameter
  StringLength(V string => rax)->setReg(rdi);                                   # Length of string to hash
  Hash();                                                                       # Hash string

  PrintOutRegisterInHex r15;

  my $e = Assemble keep => 'hash';                                              # Assemble to the specified file name
  say STDERR qx($e "");
  say STDERR qx($e "a");
  ok qx($e "")  =~ m(r15: 0000 3F80 0000 3F80);                                 # Test well known hashes
  ok qx($e "a") =~ m(r15: 0000 3F80 C000 45B2);

  if (0)                                                                        # Hash various strings
   {my %r; my %f; my $count = 0;
    my $N = RegisterSize zmm0;

    if (1)                                                                      # Fixed blocks
     {for my $l(qw(a ab abc abcd), 'a a', 'a  a')
       {for my $i(1..$N)
         {my $t = $l x $i;
          last if $N < length $t;
          my $s = substr($t.(' ' x $N), 0, $N);
          next if $f{$s}++;
          my $r = qx($e "$s");
          say STDERR "$count  $r";
          if ($r =~ m(^.*r15:\s*(.*)$)m)
           {push $r{$1}->@*, $s;
            ++$count;
           }
         }
       }
     }

    if (1)                                                                      # Variable blocks
     {for my $l(qw(a ab abc abcd), '', 'a a', 'a  a')
       {for my $i(1..$N)
         {my $t = $l x $i;
          next if $f{$t}++;
          my $r = qx($e "$t");
          say STDERR "$count  $r";
          if ($r =~ m(^.*r15:\s*(.*)$)m)
           {push $r{$1}->@*, $t;
            ++$count;
           }
         }
       }
     }
    for my $r(keys %r)
     {delete $r{$r} if $r{$r}->@* < 2;
     }

    say STDERR dump(\%r);
    say STDERR "Keys hashed: ", $count;
    confess "Duplicates : ",  scalar keys(%r);
   }

  unlink 'hash';
 }

if (1) {                                                                        #TIfEq #TIfNe #TIfLe #TIfLt #TIfGe #TIfGt
  my $cmp = sub
   {my ($a, $b) = @_;

    for my $op(qw(eq ne lt le gt ge))
     {Mov rax, $a;
      Cmp rax, $b;
      my $Op = ucfirst $op;
      eval qq(If$Op Then {PrintOutStringNL("$a $op $b")}, Else {PrintOutStringNL("$a NOT $op $b")});
      $@ and confess $@;
     }
   };
  &$cmp(1,1);
  &$cmp(1,2);
  &$cmp(3,2);
  Assemble eq => <<END, avx512=>0;
1 eq 1
1 NOT ne 1
1 NOT lt 1
1 le 1
1 NOT gt 1
1 ge 1
1 NOT eq 2
1 ne 2
1 lt 2
1 le 2
1 NOT gt 2
1 NOT ge 2
3 NOT eq 2
3 ne 2
3 NOT lt 2
3 NOT le 2
3 gt 2
3 ge 2
END
 }

#latest:;
if (1) {                                                                        #TSetMaskRegister
  Mov rax, 8;
  Mov rsi, -1;
  Inc rsi; SetMaskRegister(0, rax, rsi); PrintOutRegisterInHex k0;
  Inc rsi; SetMaskRegister(1, rax, rsi); PrintOutRegisterInHex k1;
  Inc rsi; SetMaskRegister(2, rax, rsi); PrintOutRegisterInHex k2;
  Inc rsi; SetMaskRegister(3, rax, rsi); PrintOutRegisterInHex k3;
  Inc rsi; SetMaskRegister(4, rax, rsi); PrintOutRegisterInHex k4;
  Inc rsi; SetMaskRegister(5, rax, rsi); PrintOutRegisterInHex k5;
  Inc rsi; SetMaskRegister(6, rax, rsi); PrintOutRegisterInHex k6;
  Inc rsi; SetMaskRegister(7, rax, rsi); PrintOutRegisterInHex k7;

  ok Assemble eq => <<END;
    k0: .... .... .... ...0
    k1: .... .... .... .1..
    k2: .... .... .... .3..
    k3: .... .... .... .7..
    k4: .... .... .... .F..
    k5: .... .... .... 1F..
    k6: .... .... .... 3F..
    k7: .... .... .... 7F..
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::dump  #TNasm::X86::Variable::print #TThen #TElse #TV #TK
  PrintCString  ($stdout, V(str => Rs("abc\0def")));
  PrintCStringNL($stdout, V(str => Rs("ABC\0DEF")));
  ok Assemble eq => <<END;
abcABC
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::dump  #TNasm::X86::Variable::print #TThen #TElse #TV #TK
  my $a = V(a => 3);  $a->outNL;
  my $b = K(b => 2);  $b->outNL;
  my $c = $a +  $b; $c->outNL;
  my $d = $c -  $a; $d->outNL;
  my $g = $a *  $b; $g->outNL;
  my $h = $g /  $b; $h->outNL;
  my $i = $a %  $b; $i->outNL;

  If ($a == 3,
  Then
   {PrintOutStringNL "a == 3"
   },
  Else
   {PrintOutStringNL "a != 3"
   });

  ++$a; $a->outNL;
  --$a; $a->outNL;

  ok Assemble eq => <<END, avx512=>0;
a: .... .... .... ...3
b: .... .... .... ...2
(a add b): .... .... .... ...5
((a add b) sub a): .... .... .... ...2
(a times b): .... .... .... ...6
((a times b) / b): .... .... .... ...3
(a % b): .... .... .... ...1
a == 3
a: .... .... .... ...4
a: .... .... .... ...3
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::for
  V(limit => 10)->for(sub
   {my ($i, $start, $next, $end) = @_;
    $i->outNL;
   });

  ok Assemble(debug => 0, eq => <<END, avx512=>0);
index: .... .... .... ...0
index: .... .... .... ...1
index: .... .... .... ...2
index: .... .... .... ...3
index: .... .... .... ...4
index: .... .... .... ...5
index: .... .... .... ...6
index: .... .... .... ...7
index: .... .... .... ...8
index: .... .... .... ...9
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::min #TNasm::X86::Variable::max
  my $a = V("a", 1);
  my $b = V("b", 2);
  my $c = $a->min($b);
  my $d = $a->max($b);
  $a->outNL;
  $b->outNL;
  $c->outNL;
  $d->outNL;

  ok Assemble(debug => 0, eq => <<END, avx512=>0);
a: .... .... .... ...1
b: .... .... .... ...2
min: .... .... .... ...1
max: .... .... .... ...2
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::setMask
  my $start  = V("Start",  7);
  my $length = V("Length", 3);
  $start->setMask($length, k7);
  PrintOutRegisterInHex k7;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
    k7: .... .... .... .380
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::setZmm
  my $s = Rb(0..128);
  my $source = V(Source=> $s);

  if (1)                                                                        # First block
   {$source->setZmm(0, K(key => 7), K length => 3);
   }

  if (1)                                                                        # Second block
   {$source->setZmm(0, K(key => 33), K key => 12);
   }

  PrintOutRegisterInHex zmm0;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
  zmm0: .... .... .... ...0  .... .... .... ...0 - .... ...B .A.9 .8.7  .6.5 .4.3 .2.1 .... + .... .... .... ...0  .... .... .... ...0 - .... .... .... .2.1  .... .... .... ...0
END
 }

#latest:;
if (1) {                                                                        #TLoadZmm #Tzmm
  LoadZmm 0, 0..63;
  PrintOutRegisterInHex zmm 0;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
  zmm0: 3F3E 3D3C 3B3A 3938  3736 3534 3332 3130 - 2F2E 2D2C 2B2A 2928  2726 2524 2322 2120 + 1F1E 1D1C 1B1A 1918  1716 1514 1312 1110 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
END
 }

#latest:;
if (1) {                                                                        #TgetDFromZmm #TNasm::X86::Variable::dIntoZ #TNasm::X86::Variable::bIntoZ #TNasm::X86::Variable::putWIntoZmm #TNasm::X86::Variable::qIntoZ
  my $s = Rb(0..8);
  my $c = V("Content",   "[$s]");
     $c->bIntoZ     (0, 4);
     $c->putWIntoZmm(0, 6);
     $c->dIntoZ(0, 10);
     $c->qIntoZ(0, 16);
  PrintOutRegisterInHex zmm0;
  bFromZ(zmm0, 12)->outNL;
  wFromZ(zmm0, 12)->outNL;
  dFromZ(zmm0, 12)->outNL;
  qFromZ(zmm0, 12)->outNL;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
  zmm0: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .7.6 .5.4 .3.2 .1.. - .... .3.2 .1.. ....  .1.. .... .... ....
b at offset 12 in zmm0: .... .... .... ...2
w at offset 12 in zmm0: .... .... .... .3.2
d at offset 12 in zmm0: .... .... .... .3.2
q at offset 12 in zmm0: .3.2 .1.. .... .3.2
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::bIntoX #TNasm::X86::Variable::wIntoX #TNasm::X86::Variable::dIntoX #TNasm::X86::Variable::qIntoX #TNasm::X86::Variable::bIntoZ #TNasm::X86::Variable::dIntoZ #TNasm::X86::Variable::qIntoZ #TNasm::X86::Variable::bFromZ #TNasm::X86::Variable::wFromZ #TNasm::X86::Variable::dFromZ #TNasm::X86::Variable::qFromZ #TNasm::X86::Variable::dIntoPointInZ #TNasm::X86::Variable::dFromPointInZ
  Mov rax, 0x12345678;
  my $c = V("Content", rax);
  $c->bIntoX(1, 0);
  $c->bIntoX(1, 1);
  $c->wIntoX(1, 2);
  $c->dIntoX(1, 4);
  $c->qIntoX(1, 8);
  $c->bIntoZ(1, 16);
  $c->bIntoZ(1, 17);
  $c->dIntoZ(1, 20);
  $c->qIntoZ(1, 24);
  PrintOutRegisterInHex zmm1;

  my $q = V "var";

  $q->bFromZ(1, 16); $q->outNL;
  $q->bFromZ(1, 17); $q->outNL;
  $q->wFromZ(1, 18); $q->outNL;
  $q->dFromZ(1, 20); $q->outNL;
  $q->qFromZ(1, 20); $q->outNL;

  my $p = K key => 8;
  $p->dIntoPointInZ(1, K key => 0x22);
  $p->dFromPointInZ(1)->outNL;

  ok Assemble eq => <<END;
  zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 1234 5678  1234 5678 .... 7878 - .... .... 1234 5678  1234 5678 5678 7878
var: .... .... .... ..78
var: .... .... .... ..78
var: .... .... .... ...0
var: .... .... 1234 5678
var: 1234 5678 1234 5678
d: .... .... .... ..22
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Subroutine::call

 my $h = genHash("AAAA",
   a => V(a =>  1),
   b => V(b =>  2),
   c => V(c =>  3),
   d => V(d =>  4),
   e => V(e =>  5),
   f => V(f =>  6),
   g => V(g =>  7),
   h => V(h =>  8),
   i => V(i =>  9),
   j => V(j => 10),
   k => V(k => 11),
   l => V(l => 12));

 my $i = genHash("AAAA",
   a => V(a => 0x011),
   b => V(b => 0x022),
   c => V(c => 0x033),
   d => V(d => 0x044),
   e => V(e => 0x055),
   f => V(f => 0x066),
   g => V(g => 0x077),
   h => V(h => 0x088),
   i => V(i => 0x099),
   j => V(j => 0x111),
   k => V(k => 0x222),
   l => V(l => 0x333));

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;
    my $h = $$s{h};
    my $a = $$p{a};
    $$h{a}->outNL;
    $$h{b}->outNL;
    $$h{c}->outNL;
    $$h{d}->outNL;
    $$h{e}->outNL;
    $$h{f}->outNL;
    $$h{g}->outNL;
    $$h{h}->outNL;
    $$h{i}->outNL;
    $$h{j}->outNL;
    $$h{k}->outNL;
    $$h{l}->outNL;
    $$p{b}->outNL;
   } name => "s", structures => {h => $h}, parameters=>[qw(a b)];

  $s->call(structures => {h => $i}, parameters=>{a=>V(key => 1), b=>V(key => 0x111)});
  $s->call(structures => {h => $h}, parameters=>{a=>V(key => 2), b=>V(key => 0x222)});

  Assemble eq=><<END, clocks=>9151, label => 'aa';
a: .... .... .... ..11
b: .... .... .... ..22
c: .... .... .... ..33
d: .... .... .... ..44
e: .... .... .... ..55
f: .... .... .... ..66
g: .... .... .... ..77
h: .... .... .... ..88
i: .... .... .... ..99
j: .... .... .... .111
k: .... .... .... .222
l: .... .... .... .333
b: .... .... .... .111
a: .... .... .... ...1
b: .... .... .... ...2
c: .... .... .... ...3
d: .... .... .... ...4
e: .... .... .... ...5
f: .... .... .... ...6
g: .... .... .... ...7
h: .... .... .... ...8
i: .... .... .... ...9
j: .... .... .... ...A
k: .... .... .... ...B
l: .... .... .... ...C
b: .... .... .... .222
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Subroutine::call

 my $h = genHash("AAAA",
   a => V(a =>  1),
   b => V(b =>  2),
   c => V(c =>  3),
   d => V(d =>  4),
   e => V(e =>  5),
   f => V(f =>  6),
   g => V(g =>  7),
   h => V(h =>  8),
   i => V(i =>  9),
   j => V(j => 10),
   k => V(k => 11),
   l => V(l => 12));

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;
    my $h = $$s{h};
    my $a = $$p{a};
    $$h{a}->outNL;
    $$h{b}->outNL;
    $$h{c}->outNL;
    $$h{d}->outNL;
    $$h{e}->outNL;
    $$h{f}->outNL;
    $$h{g}->outNL;
    $$h{h}->outNL;
    $$h{i}->outNL;
    $$h{j}->outNL;
    $$h{k}->outNL;
    $$h{l}->outNL;
    If $a > 0,
    Then
     {$sub->call(structures => {h => $h}, parameters=>{a=>V(key => 0), b=>V(key => 0x111)});
     };
   } name => "s", structures => {h => $h}, parameters=>[qw(a b)];

  $s->call(structures => {h => $h}, parameters=>{a=>V(key => 2), b=>V(key => 0x222)});

  Assemble eq=><<END, clocks=>17609, label => 'aaa';
a: .... .... .... ...1
b: .... .... .... ...2
c: .... .... .... ...3
d: .... .... .... ...4
e: .... .... .... ...5
f: .... .... .... ...6
g: .... .... .... ...7
h: .... .... .... ...8
i: .... .... .... ...9
j: .... .... .... ...A
k: .... .... .... ...B
l: .... .... .... ...C
a: .... .... .... ...1
b: .... .... .... ...2
c: .... .... .... ...3
d: .... .... .... ...4
e: .... .... .... ...5
f: .... .... .... ...6
g: .... .... .... ...7
h: .... .... .... ...8
i: .... .... .... ...9
j: .... .... .... ...A
k: .... .... .... ...B
l: .... .... .... ...C
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::used #TNasm::X86::Area::clear #TNasm::X86::Area::size  #TNasm::X86::Area::free
  my $a = CreateArea;

  $a->q("a" x 255);
  $a->used->outNL;
  $a->size->outNL;
  $a->dump('A');
  $a->clear;
  $a->used->outNL;
  $a->size->outNL;
  $a->dump('B');

  $a->q("a" x 4095);
  $a->used->outNL;
  $a->size->outNL;
  $a->dump('C');
  $a->clear;
  $a->used->outNL;
  $a->size->outNL;
  $a->dump('D');

  $a->free;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
area used up: .... .... .... ..FF
size of area: .... .... .... 10..
A
Area     Size:     4096    Used:      319
.... .... .... ...0 | __10 ____ ____ ____  3F.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
area used up: .... .... .... ...0
size of area: .... .... .... 10..
B
Area     Size:     4096    Used:       64
.... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
area used up: .... .... .... .FFF
size of area: .... .... .... 20..
C
Area     Size:     8192    Used:     4159
.... .... .... ...0 | __20 ____ ____ ____  3F10 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
area used up: .... .... .... ...0
size of area: .... .... .... 20..
D
Area     Size:     8192    Used:       64
.... .... .... ...0 | __20 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..80 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
.... .... .... ..C0 | 6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161  6161 6161 6161 6161
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::read
  my $a = CreateArea;
  my $f = owf q(zzzArea.txt), "AA\n";
  $a->read(V file => Rs $f);
  $a->dump("AA");

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
AA
Area     Size:     4096    Used:       67
.... .... .... ...0 | __10 ____ ____ ____  43__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 4141 .A__ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Variable::setMask
  my $z = V('zero', 0);
  my $o = V('one',  1);
  my $t = V('two',  2);
  $z->setMask($o,       k7); PrintOutRegisterInHex k7;
  $z->setMask($t,       k6); PrintOutRegisterInHex k6;
  $z->setMask($o+$t,    k5); PrintOutRegisterInHex k5;
  $o->setMask($o,       k4); PrintOutRegisterInHex k4;
  $o->setMask($t,       k3); PrintOutRegisterInHex k3;
  $o->setMask($o+$t,    k2); PrintOutRegisterInHex k2;

  $t->setMask($o,       k1); PrintOutRegisterInHex k1;
  $t->setMask($t,       k0); PrintOutRegisterInHex k0;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
    k7: .... .... .... ...1
    k6: .... .... .... ...3
    k5: .... .... .... ...7
    k4: .... .... .... ...2
    k3: .... .... .... ...6
    k2: .... .... .... ...E
    k1: .... .... .... ...4
    k0: .... .... .... ...C
END
 }

#latest:
if (1) {                                                                        #TExtern #TLink #TCallC
  my $format = Rs "Hello %s\n";
  my $data   = Rs "World";

  Extern qw(printf exit malloc strcpy); Link 'c';

  CallC 'malloc', length($format)+1;
  Mov r15, rax;
  CallC 'strcpy', r15, $format;
  CallC 'printf', r15, $data;
  CallC 'exit', 0;

  ok Assemble avx512=>0, eq => <<END;
Hello World
END
 }

#latest:
if (1) {
  my $a = Rb((reverse 0..16)x16);
  my $b = Rb((        0..16)x16);
  Mov rax, $a;  Vmovdqu8 zmm0, "[rax]";
  Mov rax, $b;  Vmovdqu8 zmm1, "[rax]";
  Vpcmpeqb k0, zmm0, zmm1;

  Kmovq rax, k0; Popcnt rax, rax;
  PrintOutRegisterInHex zmm0, zmm1, k0, rax;

  ok Assemble eq => <<END;
  zmm0: .4.5 .6.7 .8.9 .A.B  .C.D .E.F 10.. .1.2 - .3.4 .5.6 .7.8 .9.A  .B.C .D.E .F10 ...1 + .2.3 .4.5 .6.7 .8.9  .A.B .C.D .E.F 10.. - .1.2 .3.4 .5.6 .7.8  .9.A .B.C .D.E .F10
  zmm1: .C.B .A.9 .8.7 .6.5  .4.3 .2.1 ..10 .F.E - .D.C .B.A .9.8 .7.6  .5.4 .3.2 .1.. 10.F + .E.D .C.B .A.9 .8.7  .6.5 .4.3 .2.1 ..10 - .F.E .D.C .B.A .9.8  .7.6 .5.4 .3.2 .1..
    k0: .8.. .4.. .2.. .1..
   rax: .... .... .... ...4
END
 }

#latest:
if (1) {                                                                        #TConvertUtf8ToUtf32 #TGetNextUtf8CharAsUtf32 #TRutf8
  my ($out, $size, $fail);

  my $Chars = Rb(0x24, 0xc2, 0xa2, 0xc9, 0x91, 0xE2, 0x82, 0xAC, 0xF0, 0x90, 0x8D, 0x88);
  my $chars = V(chars => $Chars);

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+0;                        # Dollar               UTF-8 Encoding: 0x24                UTF-32 Encoding: 0x00000024
  $out->out('out1 : ');
  $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+1;                        # Cents                UTF-8 Encoding: 0xC2 0xA2           UTF-32 Encoding: 0x000000a2
  $out->out('out2 : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+3;                        # Alpha                UTF-8 Encoding: 0xC9 0x91           UTF-32 Encoding: 0x00000251
  $out->out('out3 : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+5;                        # Euro                 UTF-8 Encoding: 0xE2 0x82 0xAC      UTF-32 Encoding: 0x000020AC
  $out->out('out4 : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $chars+8;                        # Gothic Letter Hwair  UTF-8 Encoding  0xF0 0x90 0x8D 0x88 UTF-32 Encoding: 0x00010348
  $out->out('out5 : ');     $size->outNL(' size : ');

  my $statement = qq(𝖺\n 𝑎𝑠𝑠𝑖𝑔𝑛 【【𝖻 𝐩𝐥𝐮𝐬 𝖼】】\nAAAAAAAA);                        # A sample sentence to parse

  my $s = K(statement => Rutf8($statement));
  my $l = StringLength $s;

  my $address = AllocateMemory $l;                                              # Allocate enough memory for a copy of the string
  CopyMemory($s, $address, $l);

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address;
  $out->out('outA : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+4;
  $out->out('outB : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+5;
  $out->out('outC : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+30;
  $out->out('outD : ');     $size->outNL(' size : ');

 ($out, $size, $fail) = GetNextUtf8CharAsUtf32 $address+35;
  $out->out('outE : ');     $size->outNL(' size : ');

  $address->printOutMemoryInHexNL($l);

  ok Assemble(debug => 0, eq => <<END, avx512=>0);
out1 : .... .... .... ..24 size : .... .... .... ...1
out2 : .... .... .... ..A2 size : .... .... .... ...2
out3 : .... .... .... .251 size : .... .... .... ...2
out4 : .... .... .... 20AC size : .... .... .... ...3
out5 : .... .... ...1 .348 size : .... .... .... ...4
outA : .... .... ...1 D5BA size : .... .... .... ...4
outB : .... .... .... ...A size : .... .... .... ...1
outC : .... .... .... ..20 size : .... .... .... ...1
outD : .... .... .... ..20 size : .... .... .... ...1
outE : .... .... .... ..10 size : .... .... .... ...2
F09D 96BA .A20 F09D  918E F09D 91A0 F09D  91A0 F09D 9196 F09D  9194 F09D 919B 20E3  8090 E380 90F0 9D96  BB20 F09D 90A9 F09D  90A5 F09D 90AE F09D  90AC 20F0 9D96 BCE3  8091 E380 91.A 4141  4141 4141 4141 ....
END
 }

#latest:
if (1) {                                                                        #TLoadBitsIntoMaskRegister
  for (0..7)
   {ClearRegisters "k$_";
    K($_,$_)->setMaskBit("k$_");
    PrintOutRegisterInHex "k$_";
   }

  ClearRegisters k7;
  LoadBitsIntoMaskRegister(7, '1010', -4, +4, -2, +2, -1, +1, -1, +1);
  PrintOutRegisterInHex "k7";

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
    k0: .... .... .... ...1
    k1: .... .... .... ...2
    k2: .... .... .... ...4
    k3: .... .... .... ...8
    k4: .... .... .... ..10
    k5: .... .... .... ..20
    k6: .... .... .... ..40
    k7: .... .... .... ..80
    k7: .... .... ...A .F35
END
 }

#latest:
if (1) {                                                                        #TInsertZeroIntoRegisterAtPoint #TInsertOneIntoRegisterAtPoint
  Mov r15, 0x100;                                                               # Given a register with a single one in it indicating the desired position,
  Mov r14, 0xFFDC;                                                              # Insert a zero into the register at that position shifting the bits above that position up left one to make space for the new zero.
  Mov r13, 0xF03F;
  PrintOutRegisterInHex         r14, r15;
  InsertZeroIntoRegisterAtPoint r15, r14;
  PrintOutRegisterInHex r14;
  Or r14, r15;                                                                  # Replace the inserted zero with a one
  PrintOutRegisterInHex r14;
  InsertOneIntoRegisterAtPoint r15, r13;
  PrintOutRegisterInHex r13;
  ok Assemble(debug => 0, eq => <<END, avx512=>0);
   r14: .... .... .... FFDC
   r15: .... .... .... .1..
   r14: .... .... ...1 FEDC
   r14: .... .... ...1 FFDC
   r13: .... .... ...1 E13F
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::setOrClearTreeBits
  my $b = CreateArea;
  my $t = $b->CreateTree;

  Mov r15, 8;
  $t->setTreeBit  (31, r15); PrintOutRegisterInHex 31;
  $t->isTree      (31, r15); PrintOutZF;

  Mov r15, 16;
  $t->isTree      (31, r15); PrintOutZF;
  $t->setTreeBit  (31, r15); PrintOutRegisterInHex 31;
  $t->clearTreeBit(31, r15); PrintOutRegisterInHex 31;
  $t->isTree      (31, r15); PrintOutZF;

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
 zmm31: .... .... ...8 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
ZF=0
ZF=1
 zmm31: .... .... ..18 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... .... ...8 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
ZF=0
END
 }

#latest:
if (1) {                                                                        # Print empty tree
  my $b = CreateArea;
  my $t = $b->CreateTree;
  $t->dump("AAAA");

  ok Assemble(debug => 0, eq => <<END, avx512=>1);
AAAA
- empty
END
 }

#latest:
if (0) {                                                                        # An example of using sigaction in x86 and x64 assembler code.  Linux on x86 requires not only a signal handler but a signal trampoline.  The following code shows how to set up a signal and its associated trampoline using sigaction or rt_sigaction.
  my $end   = Label;
  Jmp $end;                                                                     # Jump over subroutine definition
  my $start = SetLabel;
  Enter 0, 0;                                                                   # Inline code of signal handler
  Mov r15, rbp;                                                                 # Preserve the new stack frame
  Mov rbp, "[rbp]";                                                             # Restore our last stack frame
  PrintOutTraceBack '';                                                         # Print our trace back
  Mov rbp, r15;                                                                 # Restore supplied stack frame
  Exit(0);                                                                      # Exit so we do not trampoline. Exit with code zero to show that the program is functioning correctly, else L<Assemble> will report an error.
  Leave;
  Ret;
  SetLabel $end;

  Mov r15, 0;                                                                   # Push sufficient zeros onto the stack to make a struct sigaction as described in: https://www.man7.org/linux/man-pages/man2/sigaction.2.html
  Push r15 for 1..16;

  Mov r15, $start;                                                              # Actual signal handler
  Mov "[rsp]", r15;                                                             # Show as signal handler
  Mov "[rsp+0x10]", r15;                                                        # Add as trampoline as well - which is fine because we exit in the handler so this will never be called
  Mov r15, 0x4000000;                                                           # Mask to show we have a trampoline which is, apparently, required on x86
  Mov "[rsp+0x8]", r15;                                                         # Confirm we have a trampoline

  Mov rax, 13;                                                                  # Sigaction from "kill -l"
  Mov rdi, 11;                                                                  # Confirmed SIGSEGV = 11 from kill -l and tracing with sde64
  Mov rsi, rsp;                                                                 # Sigaction structure on stack
  Mov rdx, 0;                                                                   # Confirmed by trace
  Mov r10, 8;                                                                   # Found by tracing "signal.c" with sde64 it is the width of the signal set and mask. "signal.c" is reproduced below.
  Syscall;
  Add rsp, 128;

  my $s = Subroutine                                                            # Subroutine that will cause an error to occur to force a trace back to be printed
   {Mov r15, 0;
    Mov r15, "[r15]";                                                           # Try to read an unmapped memory location
   } [qw(in)], name => 'sub that causes a segv';                                # The name that will appear in the trace back

  $s->call(K(in, 42));

  ok Assemble(debug => 0, keep2 => 'signal', avx512=>0, eq => <<END, avx512=>0);# Cannot use the emulator because it does not understand signals

Subroutine trace back, depth:  1
0000 0000 0000 002A    sub that causes a segv

END

# /var/isde/sde64 -mix -ptr-check -debugtrace -- ./signal
##include <stdlib.h>
##include <stdio.h>
##include <signal.h>
##include <string.h>
##include <unistd.h>
#
#void handle_sigint(int sig)
# {exit(sig);
# }
#
#int main(void)
# {struct sigaction s;
#  memset(&s, 0, sizeof(s));
#  s.sa_sigaction = (void *)handle_sigint;
#
#  long a = 0xabcdef;
#  sigaction(SIGSEGV, &s, 0);
#  long *c = 0; *c = a;
# }
#
# gcc -finput-charset=UTF-8 -fmax-errors=7 -rdynamic -Wall -Wextra -Wno-unused-function -o signal signal.c  && /var/isde/sde64 -mix -ptr-check -debugtrace  -- ./signal; echo $?;
 }

#latest:
if (0) {                                                                        #TOnSegv
  OnSegv();                                                                     # Request a trace back followed by exit on a segv signal.

  my $t = Subroutine                                                            # Subroutine that will cause an error to occur to force a trace back to be printed
   {Mov r15, 0;
    Mov r15, "[r15]";                                                           # Try to read an unmapped memory location
   } [qw(in)], name => 'sub that causes a segv';                                # The name that will appear in the trace back

  $t->call(K(in, 42));

  ok Assemble(debug => 0, keep2 => 'signal', avx512=>0, eq => <<END, avx512=>0);# Cannot use the emulator because it does not understand signals

Subroutine trace back, depth:  1
0000 0000 0000 002A    sub that causes a segv

END
 }

#latest:
if (0) {                                                                        # R11 being disturbed by syscall 1
  Push 0x0a61;                                                                  # A followed by new line on the stack
  Mov  rax, rsp;
  Mov  rdx, 2;                                                                  # Length of string
  Mov  rsi, rsp;                                                                # Address of string
  Mov  rax, 1;                                                                  # Write
  Mov  rdi, 1;                                                                  # File descriptor
  Syscall;
  Pushfq;
  Pop rax;
  PrintOutRegisterInHex rax, r11;
  ok Assemble(debug => 0, eq => <<END, avx512=>0);
a
   rax: .... .... .... .2.2
END
 }


#latest:
if (1) {                                                                        #TNasm::X86::Variable::outCStringNL #TNasm::X86::Variable::outCString
  my $s = Rutf8 '𝝰𝝱𝝲𝝳';
  V(address => $s)->outCString; PrintOutNL;
  V(address => $s)->outCStringNL;

  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
𝝰𝝱𝝲𝝳
𝝰𝝱𝝲𝝳
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::printOutMemoryInHexNL
  my $u = Rd(ord('𝝰'), ord('𝝱'), ord('𝝲'), ord('𝝳'));
  Mov rax, $u;
  my $address = V address=>rax;
  $address->printOutMemoryInHexNL(K size => 16);

  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
70D7 .1.. 71D7 .1..  72D7 .1.. 73D7 .1..
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::printOutMemoryInHexNL
  my $v = V var => 2;

  If  $v == 0, Then {Mov rax, 0},
  Ef {$v == 1} Then {Mov rax, 1},
  Ef {$v == 2} Then {Mov rax, 2},
               Else {Mov rax, 3};
  PrintOutRegisterInHex rax;
  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
   rax: .... .... .... ...2
END
 }

#latest:
if (1) {                                                                        #TLoadRegFromMm #TSaveRegIntoMm
  Mov rax, 1; SaveRegIntoMm(zmm0, 0, rax);
  Mov rax, 2; SaveRegIntoMm(zmm0, 1, rax);
  Mov rax, 3; SaveRegIntoMm(zmm0, 2, rax);
  Mov rax, 4; SaveRegIntoMm(zmm0, 3, rax);

  LoadRegFromMm(zmm0, 0, r15);
  LoadRegFromMm(zmm0, 1, r14);
  LoadRegFromMm(zmm0, 2, r13);
  LoadRegFromMm(zmm0, 3, r12);

  PrintOutRegisterInHex ymm0, r15, r14, r13, r12;
  ok Assemble(debug => 0, eq => <<END, avx512=>1);
  ymm0: .... .... .... ...4  .... .... .... ...3 - .... .... .... ...2  .... .... .... ...1
   r15: .... .... .... ...1
   r14: .... .... .... ...2
   r13: .... .... .... ...3
   r12: .... .... .... ...4
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::copy  #TNasm::X86::Variable::copyRef
  my $a = V('a', 1);
  my $r = R('r')->copyRef($a);
  my $R = R('R')->copyRef($r);

  $a->outNL;
  $r->outNL;
  $R->outNL;

  $a->copy(2);

  $a->outNL;
  $r->outNL;
  $R->outNL;

  $r->copy(3);

  $a->outNL;
  $r->outNL;
  $R->outNL;

  $R->copy(4);

  $a->outNL;
  $r->outNL;
  $R->outNL;

  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
a: .... .... .... ...1
r: .... .... .... ...1
R: .... .... .... ...1
a: .... .... .... ...2
r: .... .... .... ...2
R: .... .... .... ...2
a: .... .... .... ...3
r: .... .... .... ...3
R: .... .... .... ...3
a: .... .... .... ...4
r: .... .... .... ...4
R: .... .... .... ...4
END
 }

#latest:
if (1) {                                                                        # Register expressions in parameter lists
  my $s = Subroutine
   {my ($p) = @_;
    $$p{p}->outNL;
   } parameters=>[qw(p)], name => 'test';

  $s->call(parameters=>{p => K key => 221});
  $s->call(parameters=>{p => V key => 222});
  Mov r15, 0xcc;
  $s->call(parameters=>{p => V(key => r15)});

  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
p: .... .... .... ..DD
p: .... .... .... ..DE
p: .... .... .... ..CC
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::clone
  my $a = V('a', 1);
  my $b = $a->clone('a');

  $_->outNL for $a, $b;

  ok Assemble(debug => 0, trace => 0, eq => <<END, avx512=>0);
a: .... .... .... ...1
a: .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #T
  my $w = 12;

  Mov rax, 0;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 0x2a;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 1;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 255;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 123456;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 1234567890;
  PrintOutRightInDecNL rax,  $w;

  Mov rax, 0x2;
  Shl rax, 16;
  Mov rdx, 0xdfdc;
  Or rax, rdx;
  Shl rax, 16;
  Mov rdx, 0x1c35;
  Or rax, rdx;
  PrintOutRightInDecNL rax,  $w;

# 1C BE99 1A14
  Mov rax, 0x1c;
  Shl rax, 16;
  Mov rdx, 0xbe99;
  Or rax, rdx;
  Shl rax, 16;
  Mov rdx, 0x1a14;
  Or rax, rdx;
  PrintOutRaxInDecNL;

# 2 EE33 3961
  Mov rax, 0x2;
  Shl rax, 16;
  Mov rdx, 0xee33;
  Or rax, rdx;
  Shl rax, 16;
  Mov rdx, 0x3961;
  Or rax, rdx;
  PrintOutRightInDecNL rax,  $w;

  ok Assemble avx512=>0, eq => <<END;
           0
          42
           1
         255
      123456
  1234567890
 12345678901
123456789012
 12586269025
END
 }

#latest:
if (1) {                                                                        #TReadChar #TPrintOutRaxAsChar #TPrintOutRaxAsCharNL #TForEver
  my $e = q(readChar);

  ForEver
   {my ($start, $end) = @_;
    ReadChar;
    Cmp rax, 0xa;
    Jle $end;
    PrintOutRaxAsChar;
    PrintOutRaxAsCharNL;
   };
  PrintOutNL;

  Assemble keep => $e;

  my $r = qx(echo "ABCDCBA" | ./$e);
  is_deeply $r, <<END;
AA
BB
CC
DD
CC
BB
AA

END
  unlink $e;
 }

#latest:
if (1) {                                                                        #TForIn
  my $remainder = r15, my $offset = r14;

  Mov $offset, 0;
  Mov $remainder, 10;

  ForIn
   {PrintOutStringNL "AAAA";
    PrintOutRegisterInHex $offset, $remainder;
   }
  Then
   {PrintOutStringNL "BBBB";
    PrintOutRegisterInHex $offset, $remainder;
   }, $offset, $remainder, 4;

  ok Assemble eq => <<END;
AAAA
   r14: .... .... .... ...0
   r15: .... .... .... ...A
AAAA
   r14: .... .... .... ...4
   r15: .... .... .... ...A
BBBB
   r14: .... .... .... ...8
   r15: .... .... .... ...2
END
 }

#latest:
if (1) {                                                                        #TuptoNTimes
  uptoNTimes
   {my ($end, $start) = @_;
    PrintOutRegisterInHex rax;
   } rax, 3;

  ok Assemble eq => <<END;
   rax: .... .... .... ...3
   rax: .... .... .... ...2
   rax: .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #TPrintOutRaxAsTextNL
  my $t = Rs('abcdefghi');
  Mov rax, $t;
  Mov rax, "[rax]";
  PrintOutRaxAsTextNL;
  ok Assemble eq => <<END, avx512=>0;
abcdefgh
END
}

#latest:
if (1) {                                                                        #TNasm::X86::Variable::outCStringNL #TNasm::X86::Variable::outInDecNL;
  my $e = q(parameters);

  (V string => "[rbp+8]")->outInDecNL;
  (V string => "[rbp+16]")->outCStringNL;
  (V string => "[rbp+24]")->outCStringNL;
  (V string => "[rbp+32]")->outCStringNL;
  (V string => "[rbp+40]")->outCStringNL;
  (V string => "[rbp+48]")->outInDecNL;

  (V string => "[rbp+8]")->for(sub
   {my ($index, $start, $next, $end) = @_;
    $index->setReg(rax);
    Inc rax;
    PrintOutRaxInDec;
    Inc rax;
    PrintOutString " : ";
    Shl rax, 3;
    (V string => "[rbp+rax]")->outCStringNL;
   });

  Assemble keep => $e;

  is_deeply scalar(qx(./$e AaAaAaAaAa BbCcDdEe 123456789)), <<END;
string: 4
./parameters
AaAaAaAaAa
BbCcDdEe
123456789
string: 0
1 : ./parameters
2 : AaAaAaAaAa
3 : BbCcDdEe
4 : 123456789
END

  unlink $e;
 }

#latest:
if (1) {                                                                        #TPrintOutRaxAsTextNL
  K( loop => 16)->for(sub
   {my ($index, $start, $next, $end) = @_;
    $index->setReg(rax);
    Add rax, 0xb0;   Shl rax, 16;
    Mov  ax, 0x9d9d; Shl rax, 8;
    Mov  al, 0xf0;
    PrintOutRaxAsText;
   });
  PrintOutNL;

  ok Assemble eq => <<END, avx512 => 0;
𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿
END
 }

#latest:
if (1) {                                                                        #TPrintOutRightInHexNL #TPrintOutRightInDecNL #TPrintOutRightInBinNL
  Mov rax, 0x2a;
  PrintOutRightInDecNL rax, 16;
  PrintOutRightInHexNL rax, 16;
  PrintOutRightInBinNL rax, 16;

  ok Assemble eq => <<END, avx512=>1;
              42
              2A
          101010
END
 }

#latest:
if (1) {                                                                        # Fibonacci numbers
  my $N = 11;                                                                   # The number of Fibonacci numbers to generate
  Mov r13, 0;                                                                   # First  Fibonacci number
  Mov r14, 1;                                                                   # Second Fibonacci
  PrintOutStringNL " i   Fibonacci";                                            # The title of the piece

  V(N => $N)->for(sub                                                           # Generate each Fibonacci number by adding the two previous ones together
   {my ($index, $start, $next, $end) = @_;
    $index->outRightInDec(2);                                                   # Index
    Mov rax, r13;
    PrintOutRightInDecNL rax, 12;                                               # Fibonacci number at this index

    Mov r15, r14;                                                               # Next number is the sum of the two previous ones
    Add r15, r13;

    Mov r13, r14;                                                               # Move up
    Mov r14, r15;
   });

  ok Assemble eq => <<END, avx512=>0;
 i   Fibonacci
 0           0
 1           1
 2           1
 3           2
 4           3
 5           5
 6           8
 7          13
 8          21
 9          34
10          55
END
 }

#latest:
if (1) {                                                                        #TReadLine
  my $e = q(readLine);
  my $f = writeTempFile("hello\nworld\n");

  ReadLine;
  PrintOutRaxAsTextNL;
  ReadLine;
  PrintOutRaxAsTextNL;

  Assemble keep => $e;

  is_deeply scalar(qx(./$e < $f)), <<END;
hello
world
END
  unlink $f;
}

#latest:
if (1) {                                                                        #TReadInteger
  my $e = q(readInteger);
  my $f = writeTempFile("11\n22\n");

  ReadInteger;
  Shl rax, 1;
  PrintOutRaxInDecNL;
  ReadInteger;
  Shl rax, 1;
  PrintOutRaxInDecNL;

  Assemble keep => $e;

  is_deeply scalar(qx(./$e < $f)), <<END;
22
44
END

  unlink $e, $f;
 }

#latest:
if (1) {                                                                        #TSubroutine
  package InnerStructure                                                        # Test passing structures into a subroutine
   {use Data::Table::Text qw(:all);
    sub new($)                                                                  # Create a new structure
     {my ($value) = @_;                                                         # Value for structure variable
      describe(value => Nasm::X86::V(var => $value))
     };
    sub describe(%)                                                             # Describe the components of a structure
     {my (%options) = @_;                                                       # Options
      genHash(__PACKAGE__,
        value => $options{value},
       );
     }
   }

  package OuterStructure
   {use Data::Table::Text qw(:all);
    sub new($$)                                                                 # Create a new structure
     {my ($valueOuter, $valueInner) = @_;                                       # Value for structure variable
      describe
       (value => Nasm::X86::V(var => $valueOuter),
        inner => InnerStructure::new($valueInner),
       )
     };
    sub describe(%)                                                             # Describe the components of a structure
     {my (%options) = @_;                                                       # Options
      genHash(__PACKAGE__,
        value => $options{value},
        inner => $options{inner},
       );
     }
   }

  my $t = OuterStructure::new(42, 4);

  my $s = Subroutine
   {my ($parameters, $structures, $sub) = @_;                                   # Variable parameters, structure variables, structure copies, subroutine description

    $$structures{test}->value->setReg(rax);
    Mov r15, 84;
    $$structures{test}->value->getReg(r15);
    Mov r15, 8;
    $$structures{test}->inner->value->getReg(r15);

    $$parameters{p}->setReg(rdx);
   } parameters=>[qw(p)], structures => {test => $t}, name => 'test';

  my $T = OuterStructure::new(42, 4);
  my $V = V parameter => 21;

  $s->call(parameters=>{p => $V}, structures=>{test => $T});

  PrintOutRaxInDecNL;
  Mov rax, rdx;
  PrintOutRaxInDecNL;
  $t->value->outInDecNL;
  $t->inner->value->outInDecNL;
  $T->value->outInDecNL;
  $T->inner->value->outInDecNL;
  ok Assemble eq => <<END, avx512=>0;
42
21
var: 42
var: 4
var: 84
var: 8
END
 }

#latest:
if (1) {
  my $s = Subroutine                                                            #TSubroutine2
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    $$s{var}->setReg(rax);
    Dec rax;
    $$s{var}->getReg(rax);
   } structures => {var => my $v = V var => 42}, name => 'test';

  $v->outNL;

  $s->call(structures => {var => my $V = V var => 2});
  $V->outNL;

  ok Assemble eq => <<END;
var: .... .... .... ..2A
var: .... .... .... ...1
END
 }

#latest:
if (1) {
  my $N = 256;
  my $t = V struct => 33;

  my $s = Subroutine                                                            #TSubroutine
   {my ($p, $s, $sub) = @_;                                                     # Variable parameters, structure variables, structure copies, subroutine description
    SaveFirstFour;
    my $v = V var => 0;
    $v->copy($$p{i});
    $$p{o}->copy($v);
    $$p{O}->copy($$s{struct});
    $$s{struct}->copy($$s{struct} + 1);

    my $M = AllocateMemory K size => $N;                                        # Allocate memory and save its location in a variable
    $$p{M}->copy($M);
    $M->setReg(rax);
    Mov "qword[rax]", -1;
    FreeMemory $M, K size => $N;                                                # Free memory
    RestoreFirstFour;
   } structures => {struct => $t}, parameters => [qw(i o O M)], name => 'test';

  $s->call(parameters => {i => (my $i = K i => 22),
                          o => (my $o = V o =>  0),
                          O => (my $O = V O =>  0),
                          M => (my $M = V M =>  0)},
           structures => {struct => $t});
  $i->outInDecNL;
  $o->outInDecNL;
  $O->outInDecNL;
  $t->outInDecNL;

  ok Assemble eq => <<END;
i: 22
o: 22
O: 33
struct: 34
END
 }

test2: goto test3 unless $test{2};

#latest:;
if (1) {                                                                        #TbFromX #TbFromZ #TbRegFromZmm #TbRegIntoZmm #TdFromPointInZ #TdFromX #TdFromZ #TdRegFromZmm #TdRegIntoZmm #TqFromX #TqFromZ #TwFromX #TwFromZ #TwRegFromZmm #TwRegIntoZmm #Txmm #Tymm #TzmmM #TzmmMZ
  Mov r15, 0x12345678;
  bRegIntoZmm(r15, 1,  0);
  bRegIntoZmm(r15, 1,  1);
  dRegIntoZmm(r15, 1,  4);
  dRegIntoZmm(r15, 1,  8);
  dRegIntoZmm(r15, 1, 12);
  PrintOutRegisterInHex xmm(1);
  PrintOutRegisterInHex zmm(1);

  bRegFromZmm(r15, 1, 1);
  PrintOutRegisterInHex r15;

  bFromX(1, 0)->outNL;
  bFromZ(1, 0)->outNL;

  dRegFromZmm(r14, 1, 3);
  PrintOutRegisterInHex r14;

  wRegFromZmm(r14, 1, 3);
  PrintOutRegisterInHex r14;

  bFromX(1, 0)->outNL;
  bFromZ(1, 1)->outNL;
  bFromZ(1, 2)->outNL;

  wFromX(1, 0)->outNL;
  wFromZ(1, 1)->outNL;
  wFromZ(1, 2)->outNL;

  dFromX(1, 0)->outNL;
  dFromZ(1, 1)->outNL;
  dFromZ(1, 2)->outNL;

  qFromX(1, 0)->outNL;
  qFromZ(1, 1)->outNL;
  dFromZ(1, 2)->outNL;

  K( offset => 1 << 5)->dFromPointInZ(zmm(1))->outNL;

  Mov       r15, 2;
  Kmovq k7, r15;
  LoadZmm 2, map {0xff} 1..64;
  PrintOutRegisterInHex zmm2;
  Vmovdqu8 zmmM (2, 7), zmm(1);
  Vmovdqu8 zmmMZ(3, 7), zmm(1);
  PrintOutRegisterInHex zmm1, zmm2, zmm3;

  ok Assemble eq => <<END;
  xmm1: 1234 5678 1234 5678  1234 5678 .... 7878
  zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
   r15: .... .... 1234 5678
b at offset 0 in xmm1: .... .... .... ..78
b at offset 0 in zmm1: .... .... .... ..78
   r14: .... .... 3456 78..
   r14: .... .... 3456 78..
b at offset 0 in xmm1: .... .... .... ..78
b at offset 1 in zmm1: .... .... .... ..78
b at offset 2 in zmm1: .... .... .... ...0
w at offset 0 in xmm1: .... .... .... 7878
w at offset 1 in zmm1: .... .... .... ..78
w at offset 2 in zmm1: .... .... .... ...0
d at offset 0 in xmm1: .... .... .... 7878
d at offset 1 in zmm1: .... .... 78.. ..78
d at offset 2 in zmm1: .... .... 5678 ....
q at offset 0 in xmm1: 1234 5678 .... 7878
q at offset 1 in zmm1: 7812 3456 78.. ..78
d at offset 2 in zmm1: .... .... 5678 ....
d: .... .... .... ...0
  zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1
  zmm1: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - 1234 5678 1234 5678  1234 5678 .... 7878
  zmm2: .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  .... .... .... ..-1 + .... .... .... ..-1  .... .... .... ..-1 - .... .... .... ..-1  FFFF FFFF FFFF 78FF
  zmm3: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... 78..
END
 }

#latest:;
if (1) {                                                                        #TopposingJump
  is_deeply opposingJump(q(Jl)), q(Jge);
  is_deeply opposingJump(q(Jg)), q(Jle);
 }

#latest:
if (1) {                                                                        # Split a left node held in zmm28..zmm26 with its parent in zmm31..zmm29 pushing to the right zmm25..zmm23
  my $newRight = K newRight => 0x9119;                                          # Offset of new right block
  my $tree = DescribeTree(length => 3);                                         # Test with a narrow tree
  my ($RN, $RD, $RK, $LN, $LD, $LK, $PN, $PD, $PK) = 23..31;                    # Zmm names
  my $transfer = r8;

  for my $test(0..13)                                                           # Test each key position
   {PrintOutStringNL "Test $test";

    K(PK => Rd(map {($_<<28) +0x9999999} 1..15, 0))->loadZmm($PK);
    K(PD => Rd(map {($_<<28) +0x7777777} 1..15, 0))->loadZmm($PD);
    K(PN => Rd(map {($_<<28) +0x8888888} 1..15, 0))->loadZmm($PN);

    K(LK => Rd(map {($_<<28) +0x6666666} $test..15, 0..($test-1)))->loadZmm($LK);
    K(LD => Rd(map {($_<<28) +0x4444444} $test..15, 0..($test-1)))->loadZmm($LD);
    K(LN => Rd(map {($_<<28) +0x5555555} 0..15))->loadZmm($LN);

    K(RK => Rd(map {($_<<28) +0x3333333} 0..15))->loadZmm($RK);
    K(RD => Rd(map {($_<<28) +0x1111111} 0..15))->loadZmm($RD);
    K(RN => Rd(map {($_<<28) +0x2222222} 0..15))->loadZmm($RN);

    Mov $transfer, 0;                                                           # Test set of tree bits
    wRegIntoZmm $transfer, $PK, $tree->treeBits;

    Mov $transfer, 1;                                                           # Test set of parent length
    wRegIntoZmm $transfer, $PK, $tree->lengthOffset;

    Mov $transfer, 0b11011101;                                                  # Test set of tree bits in node being split
    wRegIntoZmm $transfer, $LK, $tree->treeBits;

    $tree->splitNotRoot($newRight, reverse 23..31);

    PrintOutStringNL "Parent";
    PrintOutRegisterInHex zmm reverse 29..31;

    PrintOutStringNL "Left";
    PrintOutRegisterInHex zmm reverse 26..28;

    PrintOutStringNL "Right";
    PrintOutRegisterInHex zmm reverse 23..25;
   }

  ok Assemble eq => <<END, , label=>'t2';
Test 0
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  6666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  6444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: F666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5666 6666 4666 6666 - 3666 6666 2666 6666  1666 6666 .666 6666
 zmm27: F444 4444 E444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5444 4444 4444 4444 - 3444 4444 2444 4444  1444 4444 .444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C666 6666 B666 6666 - A666 6666 9666 6666  8666 6666 7666 6666
 zmm24: F111 1111 E444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C444 4444 B444 4444 - A444 4444 9444 4444  8444 4444 7444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 1
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  7666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  7444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: .666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  6666 6666 5666 6666 - 4666 6666 3666 6666  2666 6666 1666 6666
 zmm27: .444 4444 F444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  6444 4444 5444 4444 - 4444 4444 3444 4444  2444 4444 1444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  D666 6666 C666 6666 - B666 6666 A666 6666  9666 6666 8666 6666
 zmm24: F111 1111 F444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  D444 4444 C444 4444 - B444 4444 A444 4444  9444 4444 8444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 2
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  8666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  8444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 1666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  7666 6666 6666 6666 - 5666 6666 4666 6666  3666 6666 2666 6666
 zmm27: 1444 4444 .444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  7444 4444 6444 4444 - 5444 4444 4444 4444  3444 4444 2444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  E666 6666 D666 6666 - C666 6666 B666 6666  A666 6666 9666 6666
 zmm24: F111 1111 .444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  E444 4444 D444 4444 - C444 4444 B444 4444  A444 4444 9444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 3
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  9666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  9444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 2666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  8666 6666 7666 6666 - 6666 6666 5666 6666  4666 6666 3666 6666
 zmm27: 2444 4444 1444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  8444 4444 7444 4444 - 6444 4444 5444 4444  4444 4444 3444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  F666 6666 E666 6666 - D666 6666 C666 6666  B666 6666 A666 6666
 zmm24: F111 1111 1444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  F444 4444 E444 4444 - D444 4444 C444 4444  B444 4444 A444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 4
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  A666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  A444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 3666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  9666 6666 8666 6666 - 7666 6666 6666 6666  5666 6666 4666 6666
 zmm27: 3444 4444 2444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  9444 4444 8444 4444 - 7444 4444 6444 4444  5444 4444 4444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .666 6666 F666 6666 - E666 6666 D666 6666  C666 6666 B666 6666
 zmm24: F111 1111 2444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .444 4444 F444 4444 - E444 4444 D444 4444  C444 4444 B444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 5
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  B666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  B444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 4666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  A666 6666 9666 6666 - 8666 6666 7666 6666  6666 6666 5666 6666
 zmm27: 4444 4444 3444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  A444 4444 9444 4444 - 8444 4444 7444 4444  6444 4444 5444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  1666 6666 .666 6666 - F666 6666 E666 6666  D666 6666 C666 6666
 zmm24: F111 1111 3444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  1444 4444 .444 4444 - F444 4444 E444 4444  D444 4444 C444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 6
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  C666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  C444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 5666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  B666 6666 A666 6666 - 9666 6666 8666 6666  7666 6666 6666 6666
 zmm27: 5444 4444 4444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  B444 4444 A444 4444 - 9444 4444 8444 4444  7444 4444 6444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  2666 6666 1666 6666 - .666 6666 F666 6666  E666 6666 D666 6666
 zmm24: F111 1111 4444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  2444 4444 1444 4444 - .444 4444 F444 4444  E444 4444 D444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 7
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  D666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  D444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 6666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C666 6666 B666 6666 - A666 6666 9666 6666  8666 6666 7666 6666
 zmm27: 6444 4444 5444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C444 4444 B444 4444 - A444 4444 9444 4444  8444 4444 7444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  3666 6666 2666 6666 - 1666 6666 .666 6666  F666 6666 E666 6666
 zmm24: F111 1111 5444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  3444 4444 2444 4444 - 1444 4444 .444 4444  F444 4444 E444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 8
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  E666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  E444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 7666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  D666 6666 C666 6666 - B666 6666 A666 6666  9666 6666 8666 6666
 zmm27: 7444 4444 6444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  D444 4444 C444 4444 - B444 4444 A444 4444  9444 4444 8444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  4666 6666 3666 6666 - 2666 6666 1666 6666  .666 6666 F666 6666
 zmm24: F111 1111 6444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  4444 4444 3444 4444 - 2444 4444 1444 4444  .444 4444 F444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 9
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  F666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  F444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: 8666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  E666 6666 D666 6666 - C666 6666 B666 6666  A666 6666 9666 6666
 zmm27: 8444 4444 7444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  E444 4444 D444 4444 - C444 4444 B444 4444  A444 4444 9444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5666 6666 4666 6666 - 3666 6666 2666 6666  1666 6666 .666 6666
 zmm24: F111 1111 7444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5444 4444 4444 4444 - 3444 4444 2444 4444  1444 4444 .444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 10
Parent
 zmm31: .999 9999 ...1 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  1999 9999 .666 6666
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  1777 7777 .444 4444
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 2888 8888  .... 9119 1888 8888
Left
 zmm28: 9666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  F666 6666 E666 6666 - D666 6666 C666 6666  B666 6666 A666 6666
 zmm27: 9444 4444 8444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  F444 4444 E444 4444 - D444 4444 C444 4444  B444 4444 A444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  6666 6666 5666 6666 - 4666 6666 3666 6666  2666 6666 1666 6666
 zmm24: F111 1111 8444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  6444 4444 5444 4444 - 4444 4444 3444 4444  2444 4444 1444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 11
Parent
 zmm31: .999 9999 ...1 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  1999 9999 1666 6666
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  1777 7777 1444 4444
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 2888 8888  .... 9119 1888 8888
Left
 zmm28: A666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .666 6666 F666 6666 - E666 6666 D666 6666  C666 6666 B666 6666
 zmm27: A444 4444 9444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .444 4444 F444 4444 - E444 4444 D444 4444  C444 4444 B444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  7666 6666 6666 6666 - 5666 6666 4666 6666  3666 6666 2666 6666
 zmm24: F111 1111 9444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  7444 4444 6444 4444 - 5444 4444 4444 4444  3444 4444 2444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 12
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  2666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  2444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: B666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  1666 6666 .666 6666 - F666 6666 E666 6666  D666 6666 C666 6666
 zmm27: B444 4444 A444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  1444 4444 .444 4444 - F444 4444 E444 4444  D444 4444 C444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  8666 6666 7666 6666 - 6666 6666 5666 6666  4666 6666 3666 6666
 zmm24: F111 1111 A444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  8444 4444 7444 4444 - 6444 4444 5444 4444  4444 4444 3444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
Test 13
Parent
 zmm31: .999 9999 ...2 ...2  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  3666 6666 1999 9999
 zmm30: .777 7777 F777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  3444 4444 1777 7777
 zmm29: .888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 .... 9119  2888 8888 1888 8888
Left
 zmm28: C666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  2666 6666 1666 6666 - .666 6666 F666 6666  E666 6666 D666 6666
 zmm27: C444 4444 B444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  2444 4444 1444 4444 - .444 4444 F444 4444  E444 4444 D444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  9666 6666 8666 6666 - 7666 6666 6666 6666  5666 6666 4666 6666
 zmm24: F111 1111 B444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  9444 4444 8444 4444 - 7444 4444 6444 4444  5444 4444 4444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
END
 }

#latest:
if (1) {                                                                        # Split a root node held in zmm28..zmm26 into a parent in zmm31..zmm29 and a right node held in zmm25..zmm23
  my $transfer = r8;
  my $tree      = DescribeTree(length => 7);                                    # Tree definition

  Mov $transfer, 0b11011101;                                                    # Test set of tree bits
  wRegIntoZmm $transfer, zmm1, $tree->treeBits;
  PrintOutRegisterInHex zmm1;

  Mov $transfer, 0b11011101;                                                    # Test set of tree bits
  wRegIntoZmm $transfer, 31, $tree->treeBits;
  PrintOutRegisterInHex 31;

  ok Assemble eq => <<END, avx512=>1;
  zmm1: .... .... ..DD ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... .... ..DD ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        # Split a root node held in zmm28..zmm26 into a parent in zmm31..zmm29 and a right node held in zmm25..zmm23
  my $newLeft   = K newLeft  => 0x9119;                                         # Offset of new left  block
  my $newRight  = K newRight => 0x9229;                                         # Offset of new right block
  my $tree      = DescribeTree(length => 7);                                    # Tree definition

  my $transfer  = r8;                                                           # Transfer register
  my ($RN, $RD, $RK, $LN, $LD, $LK, $PN, $PD, $PK) = 23..31;                    # Zmm names

  K(PK => Rd(map {($_<<28) +0x9999999} 0..15))->loadZmm($PK);
  K(PD => Rd(map {($_<<28) +0x7777777} 0..15))->loadZmm($PD);
  K(PN => Rd(map {($_<<28) +0x8888888} 0..15))->loadZmm($PN);

  K(LK => Rd(map {($_<<28) +0x6666666} 0..15))->loadZmm($LK);
  K(LD => Rd(map {($_<<28) +0x4444444} 0..15))->loadZmm($LD);
  K(LN => Rd(map {($_<<28) +0x5555555} 0..15))->loadZmm($LN);

  K(RK => Rd(map {($_<<28) +0x3333333} 0..15))->loadZmm($RK);
  K(RD => Rd(map {($_<<28) +0x1111111} 0..15))->loadZmm($RD);
  K(RN => Rd(map {($_<<28) +0x2222222} 0..15))->loadZmm($RN);

  Mov $transfer, 0b11011101;                                                    # Test set of tree bits
  wRegIntoZmm $transfer, $LK, $tree->treeBits;

  Mov $transfer, 7;                                                             # Test set of length in left keys
  wRegIntoZmm $transfer, $LK, $tree->lengthOffset;
  PrintOutStringNL "Initial Parent";
  PrintOutRegisterInHex zmm reverse 29..31;

  PrintOutStringNL "Initial Left";
  PrintOutRegisterInHex zmm reverse 26..28;

  PrintOutStringNL "Initial Right";
  PrintOutRegisterInHex zmm reverse 23..25;

  $tree->splitRoot($newLeft, $newRight, reverse 23..31);

  PrintOutStringNL "Final Parent";
  PrintOutRegisterInHex zmm reverse 29..31;

  PrintOutStringNL "Final Left";
  PrintOutRegisterInHex zmm reverse 26..28;

  PrintOutStringNL "Final Right";
  PrintOutRegisterInHex zmm reverse 23..25;

  ok Assemble eq => <<END, avx512=>1;
Initial Parent
 zmm31: F999 9999 E999 9999  D999 9999 C999 9999 - B999 9999 A999 9999  9999 9999 8999 9999 + 7999 9999 6999 9999  5999 9999 4999 9999 - 3999 9999 2999 9999  1999 9999 .999 9999
 zmm30: F777 7777 E777 7777  D777 7777 C777 7777 - B777 7777 A777 7777  9777 7777 8777 7777 + 7777 7777 6777 7777  5777 7777 4777 7777 - 3777 7777 2777 7777  1777 7777 .777 7777
 zmm29: F888 8888 E888 8888  D888 8888 C888 8888 - B888 8888 A888 8888  9888 8888 8888 8888 + 7888 8888 6888 8888  5888 8888 4888 8888 - 3888 8888 2888 8888  1888 8888 .888 8888
Initial Left
 zmm28: F666 6666 ..DD ...7  D666 6666 C666 6666 - B666 6666 A666 6666  9666 6666 8666 6666 + 7666 6666 6666 6666  5666 6666 4666 6666 - 3666 6666 2666 6666  1666 6666 .666 6666
 zmm27: F444 4444 E444 4444  D444 4444 C444 4444 - B444 4444 A444 4444  9444 4444 8444 4444 + 7444 4444 6444 4444  5444 4444 4444 4444 - 3444 4444 2444 4444  1444 4444 .444 4444
 zmm26: F555 5555 E555 5555  D555 5555 C555 5555 - B555 5555 A555 5555  9555 5555 8555 5555 + 7555 5555 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Initial Right
 zmm25: F333 3333 E333 3333  D333 3333 C333 3333 - B333 3333 A333 3333  9333 3333 8333 3333 + 7333 3333 6333 3333  5333 3333 4333 3333 - 3333 3333 2333 3333  1333 3333 .333 3333
 zmm24: F111 1111 E111 1111  D111 1111 C111 1111 - B111 1111 A111 1111  9111 1111 8111 1111 + 7111 1111 6111 1111  5111 1111 4111 1111 - 3111 1111 2111 1111  1111 1111 .111 1111
 zmm23: F222 2222 E222 2222  D222 2222 C222 2222 - B222 2222 A222 2222  9222 2222 8222 2222 + 7222 2222 6222 2222  5222 2222 4222 2222 - 3222 2222 2222 2222  1222 2222 .222 2222
Final Parent
 zmm31: F999 9999 ...1 ...1  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... 6666 6666
 zmm30: F777 7777 E777 7777  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... 6444 4444
 zmm29: F888 8888 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... 9229 .... 9119
Final Left
 zmm28: F666 6666 ..1D ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5666 6666 4666 6666 - 3666 6666 2666 6666  1666 6666 .666 6666
 zmm27: F444 4444 E444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  5444 4444 4444 4444 - 3444 4444 2444 4444  1444 4444 .444 4444
 zmm26: F555 5555 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... 6555 5555  5555 5555 4555 5555 - 3555 5555 2555 5555  1555 5555 .555 5555
Final Right
 zmm25: F333 3333 ...1 ...6  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C666 6666 B666 6666 - A666 6666 9666 6666  8666 6666 7666 6666
 zmm24: F111 1111 E444 4444  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  C444 4444 B444 4444 - A444 4444 9444 4444  8444 4444 7444 4444
 zmm23: F222 2222 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... D555 5555  C555 5555 B555 5555 - A555 5555 9555 5555  8555 5555 7555 5555
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::setTree  #TNasm::X86::Tree::clearTree #TNasm::X86::Tree::insertZeroIntoTreeBits #TNasm::X86::Tree::insertOneIntoTreeBits #TNasm::X86::Tree::getTreeBits #TNasm::X86::Tree::setTreeBits #TNasm::X86::Tree::isTree

  my $t = DescribeTree;
  Mov r8, 0b100; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
  Mov r8, 0b010; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
  Mov r8, 0b001; $t->setTreeBit(31, r8);              PrintOutRegisterInHex 31;
  Mov r8, 0b010; $t->clearTreeBit(31, r8);            PrintOutRegisterInHex 31;

                                                     $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
  Mov r8, 0b010; $t->insertZeroIntoTreeBits(31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);
  Mov r8, 0b010; $t->insertOneIntoTreeBits (31, r8); $t->getTreeBits(31, r8); V(TreeBits => r8)->outRightInBinNL(16);

  $t->getTreeBits(31, r8);
  V(TreeBits => r8)->outRightInHexNL(4);
  PrintOutRegisterInHex 31;

  Mov r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;
  Shl r8, 1; $t->isTree(31, r8); PrintOutZF;

  Not r8; $t->setTreeBits(31, r8); PrintOutRegisterInHex 31;

  ok Assemble eq => <<END, avx512=>1;
 zmm31: .... .... ...4 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... .... ...6 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... .... ...7 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... .... ...5 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
             101
            1001
           10011
  13
 zmm31: .... .... ..13 ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
ZF=0
ZF=0
ZF=1
ZF=1
ZF=0
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
ZF=1
 zmm31: .... .... 3FFF ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Area::CreateTree
  my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->put (K(key => 2), K(data => 0x22));
  $t->find(K key => 2);
  $t->found->outNL;
  $t->data ->outNL;

  ok Assemble eq => <<END, avx512=>1;
found  : .... .... .... ...1
data   : .... .... .... ..22
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put (K(key=>2), K(data=>0x22));
  $t->put (K(key=>1), K(data=>0x11));
  $t->find(K key => 1);
  $t->found->outNL;
  $t->data ->outNL;
  $t->find(K key => 2);
  $t->found->outNL;
  $t->data ->outNL;
  $t->find(K key => 3);
  $t->found->outNL;
  $t->data ->outNL;

  ok Assemble eq => <<END, avx512=>1;
found  : .... .... .... ...1
data   : .... .... .... ..11
found  : .... .... .... ...2
data   : .... .... .... ..22
found  : .... .... .... ...0
data   : .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put   (K(key => 1), K(key => 0x11));
  $t->delete(K key => 1);
  $t->size->outNL;

  ok Assemble eq => <<END, avx512=>1;
size of tree: .... .... .... ...0
END
 }

if (1) {                                                                        #TNasm::X86::Tree::findSubTree
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;

  $t->put(1, $T);
  my $u = $t->findSubTree(1);
  $T->first->outNL;
  $u->first->outNL;

  ok Assemble eq => <<END, avx512=>1;
first  : .... .... .... ..80
first  : .... .... .... ..80
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::allocBlock #TNasm::X86::Tree::putBlock #TNasm::X86::Tree::getBlock #TNasm::X86::Tree::root
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $b = $t->allocBlock(31, 30, 29);
  K(data => 0x33)->dIntoZ(31, 4);
  $t->lengthIntoKeys(31, K length =>0x9);
  $t->putBlock($b, 31, 30, 29);
  $t->getBlock($b, 25, 24, 23);
  PrintOutRegisterInHex 25;
  $t->lengthFromKeys(25)->outNL;


  $t->firstFromMemory(28);
  $t->incSizeInFirst (28);
  $t->rootIntoFirst  (28, K value => 0x2222);
  $t->root           (28, K value => 0x2222);  PrintOutZF;
  $t->root           (28, K value => 0x2221);  PrintOutZF;
  $t->root           (28, K value => 0x2222);  PrintOutZF;
  $t->firstIntoMemory(28);

  $t->first->outNL;
  $b->outNL;
  $a->dump("1111");
  PrintOutRegisterInHex 31, 30, 29, 28;

  If $t->leafFromNodes(29) > 0, Then {PrintOutStringNL "29 Leaf"}, Else {PrintOutStringNL "29 Branch"};
  If $t->leafFromNodes(28) > 0, Then {PrintOutStringNL "28 Leaf"}, Else {PrintOutStringNL "28 Branch"};


  ok Assemble eq => <<END, avx512=>1;
 zmm25: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
b at offset 56 in zmm25: .... .... .... ...9
ZF=1
ZF=0
ZF=1
first  : .... .... .... ..40
address: .... .... .... ..80
1111
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 2222 ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ 33__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .9__ ____ C0__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
 zmm31: .... ..C0 .... ...9  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... ..33 .... ....
 zmm30: .... .1.. .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm29: .... ..40 .... ....  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm28: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... 2222
29 Leaf
28 Branch
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::indexEq
  my $tree = DescribeTree;

  my $K = 31;

  K(K => Rd(0..15))->loadZmm($K);
  $tree->lengthIntoKeys($K, K length => 13);

  K(loop => 16)->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $f = $tree->indexEq ($index, $K);
    $index->outRightInDec( 2);
    $f    ->outRightInBin(14);
    PrintOutStringNL " |"
   });

  ok Assemble eq => <<END, avx512=>1;
 0             1 |
 1            10 |
 2           100 |
 3          1000 |
 4         10000 |
 5        100000 |
 6       1000000 |
 7      10000000 |
 8     100000000 |
 9    1000000000 |
10   10000000000 |
11  100000000000 |
12 1000000000000 |
13               |
14               |
15               |
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::indexEqLt
  my $tree = DescribeTree;

  my $K = 31, my $key = 30;

  K(K => Rd(0..15))->loadZmm($K);
  $tree->lengthIntoKeys($K, K length => 13);

  K(loop => 16)->for(sub
   {my ($index, $start, $next, $end) = @_;
    $index->setReg(rdi);
    Vpbroadcastd zmm($key), edi;

    $tree->indexEqLt ($key, $K, r15, r14);
    Pushfq;
    my $f = V key => r15;
    $index->outRightInDec( 2);
    $f    ->outRightInBin(14);
    PrintOutString   " ";
    Popfq;
    PrintOutZF;
   });

  ok Assemble eq => <<END, avx512=>1;
 0             1 ZF=0
 1            10 ZF=0
 2           100 ZF=0
 3          1000 ZF=0
 4         10000 ZF=0
 5        100000 ZF=0
 6       1000000 ZF=0
 7      10000000 ZF=0
 8     100000000 ZF=0
 9    1000000000 ZF=0
10   10000000000 ZF=0
11  100000000000 ZF=0
12 1000000000000 ZF=0
13               ZF=1
14               ZF=1
15               ZF=1
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::insertionPoint
  my $tree = DescribeTree(length => 7);

  my $K = 31;

  K(K => Rd(map {2*$_} 1..16))->loadZmm($K);
  $tree->lengthIntoKeys($K, K length => 13);

  K(loop => 32)->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $f = $tree->insertionPoint($index, $K);
    $index->outRightInDec( 2);
    $f    ->outRightInBin(16);
    PrintOutStringNL " |"
   });

  ok Assemble eq => <<END, avx512=>1;
 0               1 |
 1               1 |
 2              10 |
 3              10 |
 4             100 |
 5             100 |
 6            1000 |
 7            1000 |
 8           10000 |
 9           10000 |
10          100000 |
11          100000 |
12         1000000 |
13         1000000 |
14        10000000 |
15        10000000 |
16       100000000 |
17       100000000 |
18      1000000000 |
19      1000000000 |
20     10000000000 |
21     10000000000 |
22    100000000000 |
23    100000000000 |
24   1000000000000 |
25   1000000000000 |
26  10000000000000 |
27  10000000000000 |
28  10000000000000 |
29  10000000000000 |
30  10000000000000 |
31  10000000000000 |
END
 }

#latest:
if (1) {
  my $tree = DescribeTree;

  my $K = 31, my $key = 30;

  K(K => Rd(map {2*$_} 1..16))->loadZmm($K);
  $tree->lengthIntoKeys($K, K length => 13);

  K(loop => 32)->for(sub
   {my ($index, $start, $next, $end) = @_;
    $index->setReg(rdi);
    Vpbroadcastd zmm($key), edi;
    $tree->indexEqLt($key, $K, r15, r14);
    Pushfq;
    my $f = V key => r14;
    $index->outRightInDec( 2);
    $f    ->outRightInBin(16);
    PrintOutString   " ";
    Popfq;
    PrintOutZF;
   });

  ok Assemble eq => <<END, avx512=>1;
 0               1 ZF=1
 1               1 ZF=1
 2              10 ZF=0
 3              10 ZF=1
 4             100 ZF=0
 5             100 ZF=1
 6            1000 ZF=0
 7            1000 ZF=1
 8           10000 ZF=0
 9           10000 ZF=1
10          100000 ZF=0
11          100000 ZF=1
12         1000000 ZF=0
13         1000000 ZF=1
14        10000000 ZF=0
15        10000000 ZF=1
16       100000000 ZF=0
17       100000000 ZF=1
18      1000000000 ZF=0
19      1000000000 ZF=1
20     10000000000 ZF=0
21     10000000000 ZF=1
22    100000000000 ZF=0
23    100000000000 ZF=1
24   1000000000000 ZF=0
25   1000000000000 ZF=1
26  10000000000000 ZF=0
27  10000000000000 ZF=1
28  10000000000000 ZF=1
29  10000000000000 ZF=1
30  10000000000000 ZF=1
31  10000000000000 ZF=1
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::dFromPointInZ
  my $tree = DescribeTree(length => 7);

  my $K = 31;

  K(K => Rd(0..15))->loadZmm($K);

  PrintOutRegisterInHex zmm $K;
  K( offset => 1 << 5)->dFromPointInZ($K)->outNL;

  ok Assemble eq => <<END, avx512=>1;
 zmm31: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
d: .... .... .... ...5
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::indexEq
  my $tree = DescribeTree();
  $tree->maskForFullKeyArea(7);                                                 # Mask for full key area
  PrintOutRegisterInHex k7;
  $tree->maskForFullNodesArea(7);                                               # Mask for full nodes area
  PrintOutRegisterInHex k7;
  ok Assemble eq => <<END, avx512=>1;
    k7: .... .... .... 3FFF
    k7: .... .... .... 7FFF
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::incSizeInFirst
  my $t = DescribeTree();

  my $F = 31;
  K(key => 0xff)->dIntoZ($F, 60);
  K(key => 0xee)->dIntoZ($F, 56);
  PrintOutRegisterInHex $F;
  $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
  $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
  $t->incSizeInFirst($F); PrintOutRegisterInHex $F;
  $t->decSizeInFirst($F); PrintOutRegisterInHex $F;
  $t->decSizeInFirst($F); PrintOutRegisterInHex $F;
  $t->decSizeInFirst($F); PrintOutRegisterInHex $F;

  ok Assemble eq => <<END, avx512=>1;                                           # Once we know the insertion point we can add the key/data/subTree triple, increase the length and update the tree bits
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...2  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...3  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...2  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...1  .... .... .... ...0
 zmm31: .... ..FF .... ..EE  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        # Perform the insertion
  my $tree = DescribeTree();

  my $F = 31; my $K  = 30; my $D = 29;
  my $IK = K insert  => 0x44;
  my $ID = K insert  => 0x55;
  my $tb = K treebit => 1;                                                      # Value to insert, tree bit to insert

  K(K => Rd(0..15))->loadZmm($_) for $F, $K, $D;                                # First, keys, data
  $tree->lengthIntoKeys($K, K length => 5);                                     # Set a length
  Mov rdi, 0x3FF0;                                                              # Initial tree bits
  $tree->setTreeBits(31, rdi);                                                  # Save tree bits

  my $point = K point => 1<<3;                                                  # Show insertion point

  PrintOutStringNL "Start";
  PrintOutRegisterInHex $F, $K, $D;

  $point->setReg(r15);
  $tree->insertKeyDataTreeIntoLeaf(r15, $F, $K, $D, $IK, $ID, K subTree => 1);

  PrintOutStringNL "Inserted";
  PrintOutRegisterInHex $F, $K, $D;

  $point->setReg(r15);
  $tree->overWriteKeyDataTreeInLeaf(r15, $K, $D, $ID, $IK, K subTree => 0);

  PrintOutStringNL "Overwritten";
  PrintOutRegisterInHex $F, $K, $D;

  ok Assemble eq => <<END, avx512=>1;                                           # Once we know the insertion point we can add the key/data/subTree triple, increase the length and update the tree bits
Start
 zmm31: .... ...F 3FF0 ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...F .... ...5  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm29: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
Inserted
 zmm31: .... ...F 3FF0 ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...3  .... ...1 .... ....
 zmm30: .... ...F ...8 ...6  .... ...C .... ...B - .... ...A .... ...9  .... ...8 .... ...7 + .... ...6 .... ...5  .... ...4 .... ...3 - .... ..44 .... ...2  .... ...1 .... ....
 zmm29: .... ...F .... ...E  .... ...C .... ...B - .... ...A .... ...9  .... ...8 .... ...7 + .... ...6 .... ...5  .... ...4 .... ...3 - .... ..55 .... ...2  .... ...1 .... ....
Overwritten
 zmm31: .... ...F 3FF0 ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...3  .... ...1 .... ....
 zmm30: .... ...F .... ...6  .... ...C .... ...B - .... ...A .... ...9  .... ...8 .... ...7 + .... ...6 .... ...5  .... ...4 .... ...3 - .... ..55 .... ...2  .... ...1 .... ....
 zmm29: .... ...F .... ...E  .... ...C .... ...B - .... ...A .... ...9  .... ...8 .... ...7 + .... ...6 .... ...5  .... ...4 .... ...3 - .... ..44 .... ...2  .... ...1 .... ....
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $a->dump("0000", K depth => 6);
  $t->dump("0000");

  $t->put(1, 0x11);
  $a->dump("1111", 6);
  $t->dump("1111");

  $t->put(2, 0x22);
  $a->dump("2222", 6);
  $t->dump("2222");

  $t->put(3, 0x33);
  $a->dump("3333", 6);
  $t->dump("3333");

  $t->splitNode(K offset => 0x80);
  $a->dump("4444", 11);
  $t->dump("4444");

  ok Assemble eq => <<END, avx512=>1;
0000
Area     Size:     4096    Used:      128
.... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
0000
- empty
1111
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
1111
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    1
  Data :   17
end
2222
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .2__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .2__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
2222
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    1    2
  Data :   17   34
end
3333
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .3__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .3__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
3333
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    3
  Data :   17   34   51
end
4444
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .3__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .3__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
4444
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    3
  Data :   17   34   51
end
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Tree::put #TNasm::X86::Tree::find
 {my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->put(1, 1);
  $t->find (1);
  $t->found->outNL;
  $t->data ->outNL;

  ok Assemble eq=><<END, avx512=>1;
found  : .... .... .... ...1
data   : .... .... .... ...1
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(1, 0x11);
  $t->put(2, 0x22);
  $t->put(3, 0x33);
  $t->put(4, 0x44);
  $a->dump("4444", 11);
  $t->dump("4444");

  ok Assemble eq => <<END, avx512=>1;
4444
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ 44__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
4444
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :   17   34   51   68
end
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>1), K(data=>0x11));
  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>3), K(data=>0x33));
  $t->put(K(key=>4), K(data=>0x44));
  $t->put(K(key=>5), K(data=>0x55));
  $a->dump("5555",   K depth => 11);

  ok Assemble eq => <<END, avx512=>1;
5555
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .5__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .5__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ 44__ ____  55__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>1), K(data=>0x11));
  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>3), K(data=>0x33));
  $t->put(K(key=>4), K(data=>0x44));
  $t->put(K(key=>5), K(data=>0x55));
  $t->put(K(key=>6), K(data=>0x66));
  $a->dump("6666",   K depth => 14);

  ok Assemble eq => <<END, avx512=>1;
6666
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .6__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .6__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ 44__ ____  55__ ____ 66__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .3.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .340 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>1), K(data=>0x11));
  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>3), K(data=>0x33));
  $t->put(K(key=>4), K(data=>0x44));
  $t->put(K(key=>5), K(data=>0x55));
  $t->put(K(key=>6), K(data=>0x66));
  $t->put(K(key=>7), K(data=>0x77));
  $a->dump("7777",   K depth => 14);

  ok Assemble eq => <<END, avx512=>1;
7777
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .7__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .7__ ____ C0__ ____
.... .... .... ..C0 | 11__ ____ 22__ ____  33__ ____ 44__ ____  55__ ____ 66__ ____  77__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .3.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .340 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>1), K(data=>0x11));
  $t->dump("2222");

  ok Assemble eq => <<END, avx512=>1;
2222
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    1    2
  Data :   17   34
end
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>1), K(data=>0x11));
  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>3), K(data=>0x33));
  $t->put(K(key=>4), K(data=>0x44));
  $t->put(K(key=>5), K(data=>0x55));
  $t->put(K(key=>6), K(data=>0x66));
  $t->put(K(key=>7), K(data=>0x77));
  $t->put(K(key=>8), K(data=>0x88));
  $t->dump("8888");

  ok Assemble eq => <<END, avx512=>1;
8888
At:   80                    length:    8,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5    6    7
  Keys :    1    2    3    4    5    6    7    8
  Data :   17   34   51   68   85  102  119  136
end
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key=>8), K(data=>0x88));
  $t->put(K(key=>7), K(data=>0x77));
  $t->put(K(key=>6), K(data=>0x66));
  $t->put(K(key=>5), K(data=>0x55));
  $t->put(K(key=>4), K(data=>0x44));
  $t->put(K(key=>3), K(data=>0x33));
  $t->put(K(key=>2), K(data=>0x22));
  $t->put(K(key=>1), K(data=>0x11));
  $t->dump("8888");

  ok Assemble eq => <<END, avx512=>1;
8888
At:   80                    length:    8,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5    6    7
  Keys :    1    2    3    4    5    6    7    8
  Data :   17   34   51   68   85  102  119  136
end
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K count => 14;

  $N->for(sub
   {my ($i) = @_;
    $t->put($i, $i);
   });

  $N->for(sub
   {my ($i) = @_;
    $t->find($i); $t->found->outRightInDec(8); $t->data->outRightInDecNL(8);
   });

   ok Assemble eq => <<END, avx512=>1;
       1       0
       2       1
       4       2
       8       3
      16       4
      32       5
       1       6
       1       7
       2       8
       4       9
       8      10
      16      11
      32      12
      64      13
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::put #TNasm::X86::Tree::find
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K count => 128;

  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $l = $N-$index;
    $t->put($l, $l * 2);
    my $h = $N+$index;
    $t->put($h, $h * 2);
   });
  $t->put(K(zero=>0), K(zero=>0));
  $t->printInOrder("AAAA");

  PrintOutStringNL 'Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512';
  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $i = $index;
    my $j = $i * 2;
    my $k = $j * 2;
    my $l = $k * 2;
    my $m = $l * 2;
    my $n = $m * 2;
    my $o = $n * 2;
    my $p = $o * 2;
    my $q = $p * 2;
    $t->find($i); $i->outRightInDec(4); $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($j);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($k);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($l);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($m);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($n);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($o);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($p);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDec  (8);
    $t->find($q);                       $t->found->outRightInBin(8); $t->offset->outRightInHex(8);  $t->data->outRightInDecNL(8);
   });

   ok Assemble eq => <<END, avx512=>1;
AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
Indx   Found  Offset  Double   Found  Offset    Quad   Found  Offset    Octo   Found  Offset     *16   Found  Offset     *32   Found  Offset     *64   Found  Offset    *128   Found  Offset    *256   Found  Offset    *512
   0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0       1      80       0
   1      10      80       2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0
   2     100      80       4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0
   3    1000      80       6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0
   4   10000      80       800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0
   5  100000      80      10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0
   6 1000000      80      12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0
   710000000      80      14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0
   800000000      80      16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0
   9       1     200      18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0
  10       1    1DC0      20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0
  11      10    1DC0      22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0
  12     100    1DC0      24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0
  13    1000    1DC0      26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0
  14   10000    1DC0      28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0
  15  100000    1DC0      30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0
  16      10     200      32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0
  17       1    1C40      34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0
  18      10    1C40      36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0
  19     100    1C40      38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0
  20    1000    1C40      40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0
  21   10000    1C40      42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0
  22  100000    1C40      44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0
  23     100     200      46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0
  24       1    1AC0      48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0
  25      10    1AC0      50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0
  26     100    1AC0      52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0
  27    1000    1AC0      54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0
  28   10000    1AC0      56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0
  29  100000    1AC0      5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0
  30    1000     200      60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0
  31       1    1940      62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0
  32      10    1940      64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0
  33     100    1940      66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0
  34    1000    1940      68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0
  35   10000    1940      70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0
  36  100000    1940      7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0
  37   10000     200      74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0
  38       1    1640      76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0
  39      10    1640      78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0
  40     100    1640      80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0
  41    1000    1640      82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0
  42   10000    1640      84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0
  43  100000    1640      86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0
  44  100000     200      88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0
  45       1    14C0      90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0
  46      10    14C0      92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0
  47     100    14C0      94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0
  48    1000    14C0      96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0
  49   10000    14C0      98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0
  50  100000    14C0     100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0
  51 1000000     200     102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0
  52       1    1340     104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0
  53      10    1340     106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0
  54     100    1340     108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0
  55    1000    1340     110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0
  56   10000    1340     112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0
  57  100000    1340     114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0
  5810000000     200     116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0
  59       1    11C0     118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0
  60      10    11C0     120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0
  61     100    11C0     122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0
  62    1000    11C0     124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0
  63   10000    11C0     126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0
  64  100000    11C0     128      10     C80     256               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  6500000000     200     130      10     140     260               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  66       1    1040     132    1000     140     264               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  67      10    1040     134  100000     140     268               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  68     100    1040     136       1     380     272               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  69    1000    1040     138     100     380     276               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  70   10000    1040     140   10000     380     280               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  71  100000    1040     142      10     BC0     284               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  7200000000     200     144      10     500     288               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  73       1     EC0     146    1000     500     292               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  74      10     EC0     148  100000     500     296               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  75     100     EC0     150       1     680     300               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  76    1000     EC0     152     100     680     304               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  77   10000     EC0     154   10000     680     308               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  78  100000     EC0     156    1000     BC0     312               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  79       1     C80     158      10     800     316               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  80       1     D40     160    1000     800     320               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  81      10     D40     162  100000     800     324               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  82     100     D40     164       1     980     328               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  83    1000     D40     166     100     980     332               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  84   10000     D40     168   10000     980     336               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  85  100000     D40     170  100000     BC0     340               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  86       1    1700     172      10     B00     344               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  87       1     A40     174    1000     B00     348               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  88      10     A40     176  100000     B00     352               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  89     100     A40     178       1     E00     356               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  90    1000     A40     180     100     E00     360               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  91   10000     A40     182   10000     E00     364               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  92  100000     A40     184       1    1880     368               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  93      10    1700     186      10     F80     372               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  94       1     8C0     188    1000     F80     376               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  95      10     8C0     190  100000     F80     380               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  96     100     8C0     192       1    1100     384               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  97    1000     8C0     194     100    1100     388               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  98   10000     8C0     196   10000    1100     392               0       0               0       0               0       0               0       0               0       0               0       0               0       0
  99  100000     8C0     198     100    1880     396               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 100     100    1700     200      10    1280     400               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 101       1     740     202    1000    1280     404               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 102      10     740     204  100000    1280     408               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 103     100     740     206       1    1400     412               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 104    1000     740     208     100    1400     416               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 105   10000     740     210   10000    1400     420               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 106  100000     740     212   10000    1880     424               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 107    1000    1700     214      10    1580     428               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 108       1     5C0     216    1000    1580     432               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 109      10     5C0     218  100000    1580     436               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 110     100     5C0     220       1    17C0     440               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 111    1000     5C0     222     100    17C0     444               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 112   10000     5C0     224   10000    17C0     448               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 113  100000     5C0     226 1000000    1880     452               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 114   10000    1700     228      10    1A00     456               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 115       1     440     230    1000    1A00     460               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 116      10     440     232  100000    1A00     464               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 117     100     440     234       1    1B80     468               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 118    1000     440     236     100    1B80     472               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 119   10000     440     238   10000    1B80     476               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 120  100000     440     24000000000    1880     480               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 121  100000    1700     242      10    1D00     484               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 122       1     2C0     244    1000    1D00     488               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 123      10     2C0     246  100000    1D00     492               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 124     100     2C0     248       1    1E80     496               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 125    1000     2C0     250     100    1E80     500               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 126   10000     2C0     252   10000    1E80     504               0       0               0       0               0       0               0       0               0       0               0       0               0       0
 127  100000     2C0     254 1000000    1E80     508               0       0               0       0               0       0               0       0               0       0               0       0               0       0
END
 }

test3: goto test4 unless $test{3};

#latest:
if (1) {                                                                        #TNasm::X86::Tree::printInOrder
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K count => 128;

  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $l0 = ($N-$index) / 2;
    my $l1 = ($N+$index) / 2;
    my $h0 =  $N-$index;
    my $h1 =  $N+$index;
    $t->put($l0, $l0 * 2);
    $t->put($h1, $h1 * 2);
    $t->put($l1, $l1 * 2);
    $t->put($h0, $h0 * 2);
   });

  $t->printInOrder("AAAA");

  ok Assemble eq => <<END, label=>'t3';
AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::printInOrder
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K count => 128;

  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $l0 = ($N-$index) / 2;
    my $l1 = ($N+$index) / 2;
    my $h0 =  $N-$index;
    my $h1 =  $N+$index;
    $t->put($l0, $l0 * 2);
    $t->put($h1, $h1 * 2);
    $t->put($l1, $l1 * 2);
    $t->put($h0, $h0 * 2);
   });
  $t->printInOrder("AAAA");

  ok Assemble eq => <<END, avx512=>1;
AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
END
 }

#latest:
if (1) {
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K count => 128;

  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    my $l00 = ($N-$index) / 4;
    my $l01 = ($N+$index) / 4;
    my $h00 =  $N-$index  / 2;
    my $h01 =  $N+$index  / 2;
    my $l10 = ($N-$index) / 4 * 3;
    my $l11 = ($N+$index) / 4 * 3;
    my $h10 =  $N-$index ;
    my $h11 =  $N+$index ;
    $t->put($l00, $l00 * 2);
    $t->put($h01, $h01 * 2);
    $t->put($l01, $l01 * 2);
    $t->put($h00, $h00 * 2);
    $t->put($l10, $l10 * 2);
    $t->put($h11, $h11 * 2);
    $t->put($l11, $l11 * 2);
    $t->put($h10, $h10 * 2);
   });
  $t->printInOrder("AAAA");

  ok Assemble eq => <<END, avx512=>1;
AAAA 256:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F  40  41  42  43  44  45  46  47  48  49  4A  4B  4C  4D  4E  4F  50  51  52  53  54  55  56  57  58  59  5A  5B  5C  5D  5E  5F  60  61  62  63  64  65  66  67  68  69  6A  6B  6C  6D  6E  6F  70  71  72  73  74  75  76  77  78  79  7A  7B  7C  7D  7E  7F  80  81  82  83  84  85  86  87  88  89  8A  8B  8C  8D  8E  8F  90  91  92  93  94  95  96  97  98  99  9A  9B  9C  9D  9E  9F  A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  AA  AB  AC  AD  AE  AF  B0  B1  B2  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA  DB  DC  DD  DE  DF  E0  E1  E2  E3  E4  E5  E6  E7  E8  E9  EA  EB  EC  ED  EE  EF  F0  F1  F2  F3  F4  F5  F6  F7  F8  F9  FA  FB  FC  FD  FE  FF
END
 }

if (1) {                                                                        # Speed test for put
  my $a = CreateArea;
  my $t = $a->CreateTree;

  my $N = 33;

  K(key => $N)->for(sub
   {my ($i) = @_;                                                               # Parameters
    $t->put($i, $i);
   });

# Test          Clocks           Bytes    Total Clocks     Total Bytes      Run Time     Assembler          Perl
#    1           6_947          81_896           6_947          81_896        0.2552          0.10          0.00  Better incSizeInFirst
#    1           6_716          81_728           6_716          81_728        0.2544          0.10          0.00  Better incSizeInKeys
  ok Assemble eq => <<END, clocks=>6_716;
END
 }

#latest:
if (1) {
  my $N = 13;
  my $a = CreateArea;
  my $t = $a->CreateTree;

  my ($K, $D) = (31, 30);

  K(K => Rd( 1..16))->loadZmm($K);
  K(D => Rd(17..32))->loadZmm($D);

  $t->lengthIntoKeys($K, K length => $t->length);

  Mov r15, 0b11001100110011;
  $t->setTreeBits($K, r15);

  PrintOutStringNL "Start";
  PrintOutRegisterInHex $K, $D;

  K(loop => $N)->for(sub
   {my ($index) = @_;                                                           # Parameters
    $t->deleteFirstKeyAndData($K, $D);

    PrintOutNL;
    $index->outNL;
    PrintOutNL;
    PrintOutRegisterInHex $K, $D;
    PrintOutNL;
    $t->key    ->out("k: ", "   "); $t->data->out("d: ", "   ");
    $t->subTree->out("s: ", "   "); $t->found->outNL;
   });

  ok Assemble eq => <<END, avx512=>1;
Start
 zmm31: .... ..10 3333 ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11

index: .... .... .... ...0

 zmm31: .... ..10 1999 ...C  .... ...E .... ...D - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...4  .... ...3 .... ...2
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1C  .... ..1B .... ..1A + .... ..19 .... ..18  .... ..17 .... ..16 - .... ..15 .... ..14  .... ..13 .... ..12

k: .... .... .... ...1   d: .... .... .... ..11   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...1

 zmm31: .... ..10 .CCC ...B  .... ...E .... ...D - .... ...D .... ...D  .... ...C .... ...B + .... ...A .... ...9  .... ...8 .... ...7 - .... ...6 .... ...5  .... ...4 .... ...3
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1C .... ..1B + .... ..1A .... ..19  .... ..18 .... ..17 - .... ..16 .... ..15  .... ..14 .... ..13

k: .... .... .... ...2   d: .... .... .... ..12   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...2

 zmm31: .... ..10 .666 ...A  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...C + .... ...B .... ...A  .... ...9 .... ...8 - .... ...7 .... ...6  .... ...5 .... ...4
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1C + .... ..1B .... ..1A  .... ..19 .... ..18 - .... ..17 .... ..16  .... ..15 .... ..14

k: .... .... .... ...3   d: .... .... .... ..13   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...3

 zmm31: .... ..10 .333 ...9  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...C .... ...B  .... ...A .... ...9 - .... ...8 .... ...7  .... ...6 .... ...5
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1C .... ..1B  .... ..1A .... ..19 - .... ..18 .... ..17  .... ..16 .... ..15

k: .... .... .... ...4   d: .... .... .... ..14   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...4

 zmm31: .... ..10 .199 ...8  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...C  .... ...B .... ...A - .... ...9 .... ...8  .... ...7 .... ...6
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1C  .... ..1B .... ..1A - .... ..19 .... ..18  .... ..17 .... ..16

k: .... .... .... ...5   d: .... .... .... ..15   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...5

 zmm31: .... ..10 ..CC ...7  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...C .... ...B - .... ...A .... ...9  .... ...8 .... ...7
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1C .... ..1B - .... ..1A .... ..19  .... ..18 .... ..17

k: .... .... .... ...6   d: .... .... .... ..16   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...6

 zmm31: .... ..10 ..66 ...6  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1C - .... ..1B .... ..1A  .... ..19 .... ..18

k: .... .... .... ...7   d: .... .... .... ..17   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...7

 zmm31: .... ..10 ..33 ...5  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...C .... ...B  .... ...A .... ...9
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19

k: .... .... .... ...8   d: .... .... .... ..18   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...8

 zmm31: .... ..10 ..19 ...4  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...D .... ...C  .... ...B .... ...A
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1D .... ..1C  .... ..1B .... ..1A

k: .... .... .... ...9   d: .... .... .... ..19   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...9

 zmm31: .... ..10 ...C ...3  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...D .... ...D  .... ...C .... ...B
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1D .... ..1D  .... ..1C .... ..1B

k: .... .... .... ...A   d: .... .... .... ..1A   s: .... .... .... ...1   found  : .... .... .... ...1

index: .... .... .... ...A

 zmm31: .... ..10 ...6 ...2  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...D .... ...D  .... ...D .... ...C
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1C

k: .... .... .... ...B   d: .... .... .... ..1B   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...B

 zmm31: .... ..10 ...3 ...1  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...D .... ...D  .... ...D .... ...D
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D

k: .... .... .... ...C   d: .... .... .... ..1C   s: .... .... .... ...0   found  : .... .... .... ...1

index: .... .... .... ...C

 zmm31: .... ..10 ...1 ....  .... ...E .... ...D - .... ...D .... ...D  .... ...D .... ...D + .... ...D .... ...D  .... ...D .... ...D - .... ...D .... ...D  .... ...D .... ...D
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D + .... ..1D .... ..1D  .... ..1D .... ..1D - .... ..1D .... ..1D  .... ..1D .... ..1D

k: .... .... .... ...D   d: .... .... .... ..1D   s: .... .... .... ...1   found  : .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::shiftLeft #TNasm::X86::Variable::shiftRight
  K(loop=>16)->for(sub
   {my ($index, $start, $next, $end) = @_;
   (K(one => 1)     << $index)->outRightInBinNL(16);
   (K(one => 1<<15) >> $index)->outRightInBinNL(16);
   });

  ok Assemble eq => <<END, avx512=>1;
               1
1000000000000000
              10
 100000000000000
             100
  10000000000000
            1000
   1000000000000
           10000
    100000000000
          100000
     10000000000
         1000000
      1000000000
        10000000
       100000000
       100000000
        10000000
      1000000000
         1000000
     10000000000
          100000
    100000000000
           10000
   1000000000000
            1000
  10000000000000
             100
 100000000000000
              10
1000000000000000
               1
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::firstNode #TNasm::X86::Tree::lastNode
  my $a = CreateArea;
  my $t = $a->CreateTree;

  my ($K, $D, $N) = (31, 30, 29);

  K(K => Rd( 1..16))->loadZmm($K);
  K(K => Rd( 1..16))->loadZmm($N);

  $t->lengthIntoKeys($K, K length => $t->length);

  PrintOutRegisterInHex 31, 29;
  my $f = $t->firstNode($K, $D, $N);
  my $l = $t-> lastNode($K, $D, $N);
  $f->outNL;
  $l->outNL;

  ok Assemble eq => <<END, avx512=>1;
 zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
d at offset 0 in zmm29: .... .... .... ...1
d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::firstNode #TNasm::X86::Tree::lastNode
  my $a = CreateArea;
  my $t = $a->CreateTree;

  my ($K, $D, $N) = (31, 30, 29);

  K(K => Rd( 1..16))->loadZmm($K);
  K(K => Rd( 1..16))->loadZmm($N);

  $t->lengthIntoKeys($K, K length => $t->length);

  PrintOutRegisterInHex 31, 29;
  my $f = $t->firstNode($K, $D, $N);
  my $l = $t-> lastNode($K, $D, $N);
  $f->outNL;
  $l->outNL;

  ok Assemble eq => <<END, avx512=>1;
 zmm31: .... ..10 .... ...D  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm29: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
d at offset 0 in zmm29: .... .... .... ...1
d at offset (b at offset 56 in zmm31 times 4) in zmm29: .... .... .... ...E
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::replace
  my ($K, $D) = (31, 30);

  K(K => Rd(reverse 1..16))->loadZmm($K);
  K(K => Rd(reverse 1..16))->loadZmm($D);
  PrintOutStringNL "Start";
  PrintOutRegisterInHex $K, $D;

  my $a = CreateArea;
  my $t = $a->CreateTree;

  K(loop => 14)->for(sub
   {my ($index, $start, $next, $end) = @_;

    $t->key    ->copy($index);
    $t->data   ->copy($index * 2);
    $t->subTree->copy($index % 2);

    $t->replace(K(one=>1)<<$index, $K, $D);

    $index->outNL;
    PrintOutRegisterInHex $K, $D;
   });

  ok Assemble eq => <<END, avx512=>1;
Start
 zmm31: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ..10
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ..10
index: .... .... .... ...0
 zmm31: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...F .... ....
index: .... .... .... ...1
 zmm31: .... ...1 ...2 ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...E  .... ...2 .... ....
index: .... .... .... ...2
 zmm31: .... ...1 ...2 ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...D .... ...4  .... ...2 .... ....
index: .... .... .... ...3
 zmm31: .... ...1 ...A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...C - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...4
 zmm31: .... ...1 ...A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...B .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...5
 zmm31: .... ...1 ..2A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...A  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...6
 zmm31: .... ...1 ..2A ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...9 .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...7
 zmm31: .... ...1 ..AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...8
 zmm31: .... ...1 ..AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...7 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...9
 zmm31: .... ...1 .2AA ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ...6  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...A
 zmm31: .... ...1 .2AA ...2  .... ...3 .... ...4 - .... ...5 .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ...5 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...B
 zmm31: .... ...1 .AAA ...2  .... ...3 .... ...4 - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ...4 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...C
 zmm31: .... ...1 .AAA ...2  .... ...3 .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ...3 .... ..18 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
index: .... .... .... ...D
 zmm31: .... ...1 2AAA ...2  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
 zmm30: .... ...1 .... ...2  .... ..1A .... ..18 - .... ..16 .... ..14  .... ..12 .... ..10 + .... ...E .... ...C  .... ...A .... ...8 - .... ...6 .... ...4  .... ...2 .... ....
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::extractFirst
  my ($K, $D, $N) = (31, 30, 29);

  K(K => Rd( 1..16))       ->loadZmm($K);
  K(K => Rd( 1..16))       ->loadZmm($D);
  K(K => Rd(map {0} 1..16))->loadZmm($N);

  my $a = CreateArea;
  my $t = $a->CreateTree;

  my $p = K(one => 1) << K three => 3;
  Mov r15, 0xAAAA;
  $t->setTreeBits($K, r15);

  PrintOutStringNL "Start";
  PrintOutRegisterInHex 31, 30, 29;

  K(n=>4)->for(sub
   {my ($index, $start, $next, $end) = @_;

    $t->extractFirst($K, $D, $N);

    PrintOutStringNL "-------------";
    $index->outNL;
    PrintOutRegisterInHex 31, 30, 29;

    $t->data->outNL;
    $t->subTree->outNL;
    $t->lengthFromKeys($K)->outNL;
   });

  ok Assemble eq => <<END, avx512=>1;
Start
 zmm31: .... ..10 2AAA ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
-------------
index: .... .... .... ...0
 zmm31: .... ..10 1555 ...E  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...4  .... ...3 .... ...2
 zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...4  .... ...3 .... ...2
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
data   : .... .... .... ...1
subTree: .... .... .... ...0
b at offset 56 in zmm31: .... .... .... ...E
-------------
index: .... .... .... ...1
 zmm31: .... ..10 .AAA ...D  .... ...E .... ...E - .... ...E .... ...D  .... ...C .... ...B + .... ...A .... ...9  .... ...8 .... ...7 - .... ...6 .... ...5  .... ...4 .... ...3
 zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...D  .... ...C .... ...B + .... ...A .... ...9  .... ...8 .... ...7 - .... ...6 .... ...5  .... ...4 .... ...3
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
data   : .... .... .... ...2
subTree: .... .... .... ...1
b at offset 56 in zmm31: .... .... .... ...D
-------------
index: .... .... .... ...2
 zmm31: .... ..10 .555 ...C  .... ...E .... ...E - .... ...E .... ...E  .... ...D .... ...C + .... ...B .... ...A  .... ...9 .... ...8 - .... ...7 .... ...6  .... ...5 .... ...4
 zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...E  .... ...D .... ...C + .... ...B .... ...A  .... ...9 .... ...8 - .... ...7 .... ...6  .... ...5 .... ...4
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
data   : .... .... .... ...3
subTree: .... .... .... ...0
b at offset 56 in zmm31: .... .... .... ...C
-------------
index: .... .... .... ...3
 zmm31: .... ..10 .2AA ...B  .... ...E .... ...E - .... ...E .... ...E  .... ...E .... ...D + .... ...C .... ...B  .... ...A .... ...9 - .... ...8 .... ...7  .... ...6 .... ...5
 zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...E .... ...E  .... ...E .... ...D + .... ...C .... ...B  .... ...A .... ...9 - .... ...8 .... ...7  .... ...6 .... ...5
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
data   : .... .... .... ...4
subTree: .... .... .... ...1
b at offset 56 in zmm31: .... .... .... ...B
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::extract
  my ($K, $D, $N) = (31, 30, 29);

  K(K => Rd( 1..16))->loadZmm($K);
  K(K => Rd( 1..16))->loadZmm($D);
  K(K => Rd(map {0} 1..16))->loadZmm($N);

  my $a = CreateArea;
  my $t = $a->CreateTree;

  my $p = K(one => 1) << K three => 3;
  Mov r15, 0xAAAA;
  $t->setTreeBits($K, r15);

  PrintOutStringNL "Start";
  PrintOutRegisterInHex 31, 30, 29;

  $t->extract($p, $K, $D, $N);

  PrintOutStringNL "Finish";
  PrintOutRegisterInHex 31, 30, 29;

  ok Assemble eq => <<END, avx512=>1;
Start
 zmm31: .... ..10 2AAA ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
Finish
 zmm31: .... ..10 2AAA ...E  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...3  .... ...2 .... ...1
 zmm30: .... ..10 .... ...F  .... ...E .... ...E - .... ...D .... ...C  .... ...B .... ...A + .... ...9 .... ...8  .... ...7 .... ...6 - .... ...5 .... ...3  .... ...2 .... ...1
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::nextNode #TNasm::X86::Tree::prevNode
  my $a = CreateArea;
  my $t = $a->CreateTree;

  K(loop => 66)->for(sub
   {my ($index, $start, $next, $end) = @_;
    $t->put($index, 2 * $index);
   });
  $t->getBlock(K(offset=>0x200), 31, 30, 29);
  $t->nextNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);
  $t->prevNode(K(offset=>0x440), 31, 29)->outRightInHexNL(K width => 3);

  ok Assemble eq => <<END, avx512=>1;
500
380
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::findFirst
  my $N = K(key => 32);
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $N->for(sub
   {my ($i, $start, $next, $end) = @_;
    $t->put($i, $i);
   });

  $N->for(sub
   {my ($i, $start, $next, $end) = @_;
    $t->put($N + $i, $N + $i);
    $t->findFirst;

    If $t->key != $i,
    Then
     {PrintOutTraceBack "Reverse queue first failed at: "; $i->outNL;
     };
    $t->delete($i);
    If $t->size != $N,
    Then
     {PrintOutTraceBack "Reverse queue size failed at: "; $i->outNL;
     };
    $t->printInOrder("A");
   });

  ok Assemble eq => <<END, avx512=>1;
A  32:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20
A  32:    2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21
A  32:    3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22
A  32:    4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
A  32:    5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24
A  32:    6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25
A  32:    7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26
A  32:    8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27
A  32:    9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28
A  32:    A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29
A  32:    B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A
A  32:    C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B
A  32:    D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C
A  32:    E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D
A  32:    F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E
A  32:   10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F
A  32:   11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30
A  32:   12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31
A  32:   13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32
A  32:   14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33
A  32:   15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34
A  32:   16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35
A  32:   17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36
A  32:   18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37
A  32:   19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38
A  32:   1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39
A  32:   1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A
A  32:   1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B
A  32:   1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C
A  32:   1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D
A  32:   1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E
A  32:   20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E  3F
END
 }


#latest:
if (1) {                                                                        #TNasm::X86::Tree::findLast
  my $N = K(key => 32);
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $N->for(sub
   {my ($i, $start, $next, $end) = @_;
    $t->put($N + $i, $N + $i);
   });

  $N->for(sub
   {my ($i, $start, $next, $end) = @_;
    $t->put($N - $i, $N - $i);
    $t->findLast;
    $t->delete($t->key);
    If $t->size != $N - 1,
    Then
     {PrintOutTraceBack "Queued size failed at: "; $i->outNL;
     };
    $t->printInOrder("A");
   });

  ok Assemble eq => <<END, avx512=>1;
A  31:   20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D  3E
A  31:   1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C  3D
A  31:   1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B  3C
A  31:   1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A  3B
A  31:   1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39  3A
A  31:   1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38  39
A  31:   1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37  38
A  31:   19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36  37
A  31:   18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35  36
A  31:   17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34  35
A  31:   16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33  34
A  31:   15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32  33
A  31:   14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31  32
A  31:   13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30  31
A  31:   12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F  30
A  31:   11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E  2F
A  31:   10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D  2E
A  31:    F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C  2D
A  31:    E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B  2C
A  31:    D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A  2B
A  31:    C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29  2A
A  31:    B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28  29
A  31:    A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27  28
A  31:    9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26  27
A  31:    8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25  26
A  31:    7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24  25
A  31:    6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23  24
A  31:    5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
A  31:    4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22
A  31:    3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21
A  31:    2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20
A  31:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  my $i1 = V  k => 1; $t->put($i1, $i1);
  $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
  $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
4
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    3
  Data :    1    2    3
end
X   3:    1   2   3
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i20 = V  k => 20; $t->put($i20, $i20);
  my $i30 = V  k => 30; $t->put($i30, $i30);
  my $i40 = V  k => 40; $t->put($i40, $i40);
  my $i10 = V  k => 10; $t->put($i10, $i10);
  my $i31 = V  k => 31; $t->put($i31, $i31);
  my $i32 = V  k => 32; $t->put($i32, $i32);
  my $i33 = V  k => 33; $t->put($i33, $i33);
  $t->size->outRightInDecNL(4);  $t->dump("33"); $t->delete($i33);
  $t->size->outRightInDecNL(4);  $t->dump("40"); $t->delete($i40);
  $t->size->outRightInDecNL(4);  $t->dump("X");  $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   7
33
At:   80                    length:    7,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5    6
  Keys :    A   14   1E   1F   20   21   28
  Data :   10   20   30   31   32   33   40
end
   6
40
At:   80                    length:    6,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5
  Keys :    A   14   1E   1F   20   28
  Data :   10   20   30   31   32   40
end
   5
X
At:   80                    length:    5,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4
  Keys :    A   14   1E   1F   20
  Data :   10   20   30   31   32
end
X   5:    A  14  1E  1F  20
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k =>  0; $t->put($i1, $i1);
  my $i2 = V  k => 11; $t->put($i2, $i2);
  my $i3 = V  k => 13; $t->put($i3, $i3);
  my $i4 = V  k => 15; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4);
  $t->dump("1");
  $a->dump("AAA", K blocks => 12);
  $t->delete($i2);
  $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
1
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    0    B    D    F
  Data :    0   11   13   15
end
AAA
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ .B__ ____  .D__ ____ .F__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0__ ____
.... .... .... ..C0 | ____ ____ .B__ ____  .D__ ____ .F__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    0    D    F
  Data :    0   13   15
end
X   3:    0   D   F
END
 }


#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4);  $t->dump("1"); $a->dump("AAA", K blocks => 12); $t->delete($i1);
  $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
1
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
AAA
Area     Size:     4096    Used:      320
.... .... .... ...0 | __10 ____ ____ ____  40.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0__ ____
.... .... .... ..C0 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.1 ____
.... .... .... .1.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .180 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    2    3    4
  Data :    2    3    4
end
X   3:    2   3   4
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4); $t->dump("2"); $t->delete($i2);
  $t->size->outRightInDecNL(4); $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
2
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    3    4
  Data :    1    3    4
end
X   3:    1   3   4
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i3);
  $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
3
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    4
  Data :    1    2    4
end
X   3:    1   2   4
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
  $t->size->outRightInDecNL(4);  $t->dump("X"); $t->printInOrder("X");

  ok Assemble eq => <<END, avx512=>1;
   4
4
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
   3
X
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    3
  Data :    1    2    3
end
X   3:    1   2   3
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  $t->size->outRightInDecNL(4);  $t->dump("0"); $t->delete($i2);
  $t->size->outRightInDecNL(4);  $t->dump("2"); $t->delete($i3);
  $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i4);
  $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i1);
  $t->size->outRightInDecNL(4);  $t->dump("1");

  ok Assemble eq => <<END, avx512=>1;
   4
0
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :    1    2    3    4
end
   3
2
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    3    4
  Data :    1    3    4
end
   2
3
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    1    4
  Data :    1    4
end
   1
4
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    1
  Data :    1
end
   0
1
- empty
END
 }

test4: goto test5 unless $test{4};

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->put(   K(k=>1), K(d=>11));
  $t->put(   K(k=>2), K(d=>22));
  $t->put(   K(k=>3), K(d=>33));
  $t->delete(K k=>1);  $t->dump("1");
  $t->delete(K k=>3);  $t->dump("3");
  $t->delete(K k=>2);  $t->dump("2");
  ok Assemble eq => <<END, label=>'t4';
1
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    2    3
  Data :   22   33
end
3
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    2
  Data :   22
end
2
- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->put(   K(k=>1), K(d=>11));
  $t->put(   K(k=>2), K(d=>22));
  $t->put(   K(k=>3), K(d=>33));
  $t->put(   K(k=>4), K(d=>44));
  $t->dump("0");
  $t->delete(K k=>1);
  $t->dump("1");
  $t->delete(K k=>2);
  $t->dump("2");
  $t->delete(K k=>3);
  $t->dump("3");
  $t->delete(K k=>4);
  $t->dump("4");
  ok Assemble eq => <<END, avx512=>1;
0
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :   11   22   33   44
end
1
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    2    3    4
  Data :   22   33   44
end
2
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    3    4
  Data :   33   44
end
3
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    4
  Data :   44
end
4
- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->put(   K(k=>1), K(d=>11));
  $t->put(   K(k=>2), K(d=>22));
  $t->put(   K(k=>3), K(d=>33));
  $t->put(   K(k=>4), K(d=>44));
  $t->dump("0");
  $t->delete(K k=>3);
  $t->dump("3");
  $t->delete(K k=>4);
  $t->dump("4");
  $t->delete(K k=>2);
  $t->dump("2");
  $t->delete(K k=>1);
  $t->dump("1");
  ok Assemble eq => <<END, avx512=>1;
0
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    1    2    3    4
  Data :   11   22   33   44
end
3
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    1    2    4
  Data :   11   22   44
end
4
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    1    2
  Data :   11   22
end
2
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    1
  Data :   11
end
1
- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $i1 = V  k => 1; $t->put($i1, $i1);
  my $i2 = V  k => 2; $t->put($i2, $i2);
  my $i3 = V  k => 3; $t->put($i3, $i3);
  my $i4 = V  k => 4; $t->put($i4, $i4);
  my $i5 = V  k => 5; $t->put($i5, $i5);
  my $i6 = V  k => 6; $t->put($i6, $i6);
  my $i7 = V  k => 7; $t->put($i7, $i7);
  my $i8 = V  k => 8; $t->put($i8, $i8);
  $t->size->outRightInDecNL(4);  $t->dump("1"); $t->delete($i1);
  $t->size->outRightInDecNL(4);  $t->dump("2"); $t->delete($i2);
  $t->size->outRightInDecNL(4);  $t->dump("3"); $t->delete($i3);
  $t->size->outRightInDecNL(4);  $t->dump("4"); $t->delete($i4);
  $t->size->outRightInDecNL(4);  $t->dump("5"); $t->delete($i5);
  $t->size->outRightInDecNL(4);  $t->dump("6"); $t->delete($i6);
  $t->size->outRightInDecNL(4);  $t->dump("7"); $t->delete($i7);
  $t->size->outRightInDecNL(4);  $t->dump("8"); $t->delete($i8);
  $t->size->outRightInDecNL(4);
  $t->dump("X");

  ok Assemble eq => <<END, avx512=>1;
   8
1
At:   80                    length:    8,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5    6    7
  Keys :    1    2    3    4    5    6    7    8
  Data :    1    2    3    4    5    6    7    8
end
   7
2
At:   80                    length:    7,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5    6
  Keys :    2    3    4    5    6    7    8
  Data :    2    3    4    5    6    7    8
end
   6
3
At:   80                    length:    6,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4    5
  Keys :    3    4    5    6    7    8
  Data :    3    4    5    6    7    8
end
   5
4
At:   80                    length:    5,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3    4
  Keys :    4    5    6    7    8
  Data :    4    5    6    7    8
end
   4
5
At:   80                    length:    4,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2    3
  Keys :    5    6    7    8
  Data :    5    6    7    8
end
   3
6
At:   80                    length:    3,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1    2
  Keys :    6    7    8
  Data :    6    7    8
end
   2
7
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    7    8
  Data :    7    8
end
   1
8
At:   80                    length:    1,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0
  Keys :    8
  Data :    8
end
   0
X
- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 20;
  $N->for(sub                                                                   # Load tree
   {my ($i) = @_;
    $t->put($i, $i);
   });
  $t->size->outNL;        $t->printInOrder("AA");
  $t->delete(K k =>  0);  $t->printInOrder(" 0");
  $t->delete(K k =>  9);  $t->printInOrder(" 9");
  $t->delete(K k =>  1);  $t->printInOrder(" 1");
  $t->delete(K k =>  8);  $t->printInOrder(" 8");
  $t->delete(K k =>  2);  $t->printInOrder(" 2");
  $t->delete(K k =>  7);  $t->printInOrder(" 7");
  $t->delete(K k =>  3);  $t->printInOrder(" 3");
  $t->delete(K k =>  6);  $t->printInOrder(" 6");
  $t->delete(K k =>  4);  $t->printInOrder(" 4");
  $t->delete(K k =>  5);  $t->printInOrder(" 5");
  $t->delete(K k => 10);  $t->printInOrder("10");
  $t->delete(K k => 19);  $t->printInOrder("19");
  $t->delete(K k => 11);  $t->printInOrder("11");
  $t->delete(K k => 18);  $t->printInOrder("18");
  $t->delete(K k => 12);  $t->printInOrder("12");
  $t->delete(K k => 17);  $t->printInOrder("17");
  $t->delete(K k => 13);  $t->printInOrder("13");
  $t->delete(K k => 16);  $t->printInOrder("16");
  $t->delete(K k => 14);  $t->printInOrder("14");
  $t->delete(K k => 15);  $t->printInOrder("15");

  ok Assemble eq => <<END, avx512=>1;
size of tree: .... .... .... ..14
AA  20:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13
 0  19:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13
 9  18:    1   2   3   4   5   6   7   8   A   B   C   D   E   F  10  11  12  13
 1  17:    2   3   4   5   6   7   8   A   B   C   D   E   F  10  11  12  13
 8  16:    2   3   4   5   6   7   A   B   C   D   E   F  10  11  12  13
 2  15:    3   4   5   6   7   A   B   C   D   E   F  10  11  12  13
 7  14:    3   4   5   6   A   B   C   D   E   F  10  11  12  13
 3  13:    4   5   6   A   B   C   D   E   F  10  11  12  13
 6  12:    4   5   A   B   C   D   E   F  10  11  12  13
 4  11:    5   A   B   C   D   E   F  10  11  12  13
 5  10:    A   B   C   D   E   F  10  11  12  13
10   9:    B   C   D   E   F  10  11  12  13
19   8:    B   C   D   E   F  10  11  12
11   7:    C   D   E   F  10  11  12
18   6:    C   D   E   F  10  11
12   5:    D   E   F  10  11
17   4:    D   E   F  10
13   3:    E   F  10
16   2:    E   F
14   1:    F
15- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;
  $N->for(sub
   {my ($i) = @_;
    $t->put($i, $i);
   });
  $t->printInOrder(" 0"); $t->delete(K k =>  0);
  $t->printInOrder(" 2"); $t->delete(K k =>  2);
  $t->printInOrder(" 4"); $t->delete(K k =>  4);
  $t->printInOrder(" 6"); $t->delete(K k =>  6);
  $t->printInOrder(" 8"); $t->delete(K k =>  8);
  $t->printInOrder("10"); $t->delete(K k => 10);
  $t->printInOrder("12"); $t->delete(K k => 12);
  $t->printInOrder("14"); $t->delete(K k => 14);
  $t->printInOrder(" 1"); $t->delete(K k =>  1);
  $t->printInOrder(" 3"); $t->delete(K k =>  3);
  $t->printInOrder(" 5"); $t->delete(K k =>  5);
  $t->printInOrder(" 7"); $t->delete(K k =>  7);
  $t->printInOrder(" 9"); $t->delete(K k =>  9);
  $t->printInOrder("11"); $t->delete(K k => 11);
  $t->printInOrder("13"); $t->delete(K k => 13);
  $t->printInOrder("15"); $t->delete(K k => 15);
  $t->printInOrder("XX");

  ok Assemble eq => <<END, avx512=>1;
 0  16:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
 2  15:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
 4  14:    1   3   4   5   6   7   8   9   A   B   C   D   E   F
 6  13:    1   3   5   6   7   8   9   A   B   C   D   E   F
 8  12:    1   3   5   7   8   9   A   B   C   D   E   F
10  11:    1   3   5   7   9   A   B   C   D   E   F
12  10:    1   3   5   7   9   B   C   D   E   F
14   9:    1   3   5   7   9   B   D   E   F
 1   8:    1   3   5   7   9   B   D   F
 3   7:    3   5   7   9   B   D   F
 5   6:    5   7   9   B   D   F
 7   5:    7   9   B   D   F
 9   4:    9   B   D   F
11   3:    B   D   F
13   2:    D   F
15   1:    F
XX- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K max => 8;

  $N->for(sub                                                                   # Load tree
   {my ($i) = @_;
    $t->put(         $i,     2 *        $i);
    $t->put(2 * $N - $i - 1, 2 * ($N -  $i));
   });
#  $t->printInOrder("Full");

  ($N-1)->for(sub                                                               # Delete elements
   {my ($i) = @_;
    my $n1 = ($N + $i)->clone("1111"); my $n2 = ($N - $i - 1)->clone("2222");

    $n1->outNL;
    $t->delete($n1);
    $t->printInOrder("1111");

    $n2->outNL;
    $t->delete($n2);
    $t->printInOrder("2222");
   });
  $t->dump("Two:");
  $t->size->outRightInDecNL(4);

  ok Assemble eq => <<END, avx512=>1;
1111: .... .... .... ...8
1111  15:    0   1   2   3   4   5   6   7   9   A   B   C   D   E   F
2222: .... .... .... ...7
2222  14:    0   1   2   3   4   5   6   9   A   B   C   D   E   F
1111: .... .... .... ...9
1111  13:    0   1   2   3   4   5   6   A   B   C   D   E   F
2222: .... .... .... ...6
2222  12:    0   1   2   3   4   5   A   B   C   D   E   F
1111: .... .... .... ...A
1111  11:    0   1   2   3   4   5   B   C   D   E   F
2222: .... .... .... ...5
2222  10:    0   1   2   3   4   B   C   D   E   F
1111: .... .... .... ...B
1111   9:    0   1   2   3   4   C   D   E   F
2222: .... .... .... ...4
2222   8:    0   1   2   3   C   D   E   F
1111: .... .... .... ...C
1111   7:    0   1   2   3   D   E   F
2222: .... .... .... ...3
2222   6:    0   1   2   D   E   F
1111: .... .... .... ...D
1111   5:    0   1   2   E   F
2222: .... .... .... ...2
2222   4:    0   1   E   F
1111: .... .... .... ...E
1111   3:    0   1   F
2222: .... .... .... ...1
2222   2:    0   F
Two:
At:   80                    length:    2,  data:   C0,  nodes:  100,  first:   40, root, leaf
  Index:    0    1
  Keys :    0    F
  Data :    0   16
end
   2
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K max => 100;

  $N->for(sub                                                                   # Load tree
   {my ($index, $start, $next, $end) = @_;
    $t->put($index, 2 * $index);
    If $t->size != $index + 1,
    Then
     {PrintOutStringNL "SSSS"; $index->outNL; Exit(0);
     };
   });

  $N->for(sub                                                                   # Check elements
   {my ($i) = @_;
    $t->find($i);
    If $t->found == 0,
    Then
     {PrintOutStringNL "AAAA"; $i->outNL; Exit(0);
     };
   });

  $N->for(sub                                                                   # Delete elements
   {my ($i) = @_;
    $t->delete($i);

    If $t->size != $N - $i - 1,
    Then
     {PrintOutStringNL "TTTT"; $i->outNL; Exit(0);
     };

    $N->for(sub                                                                 # Check elements
     {my ($j) = @_;
      $t->find($j);
      If $t->found == 0,
      Then
       {If $j > $i,
        Then
         {PrintOutStringNL "BBBBB"; $j->outNL; Exit(0);                         # Not deleted yet so it should be findable
         };
       },
      Else
       {If $j <= $i,
        Then
         {PrintOutStringNL "CCCCC"; $j->outNL; Exit(0);                         # Deleted so should not be findable
         };
       };
     });
   });

  ok Assemble eq => <<END, avx512=>1;
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;
  $N->for(sub
   {my ($i) = @_;
    $t->put($i, $i);
   });
  ($N/2)->for(sub
   {my ($i) = @_;
    $t->printInOrder("AAAA");
    $t->delete($i * 2);
   });
  ($N/2)->for(sub
   {my ($i) = @_;
    $t->printInOrder("BBBB");
    $t->delete($i * 2 + 1);
   });
  $t->printInOrder("CCCC");

  ok Assemble eq => <<END, avx512=>1;
AAAA  16:    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
AAAA  15:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
AAAA  14:    1   3   4   5   6   7   8   9   A   B   C   D   E   F
AAAA  13:    1   3   5   6   7   8   9   A   B   C   D   E   F
AAAA  12:    1   3   5   7   8   9   A   B   C   D   E   F
AAAA  11:    1   3   5   7   9   A   B   C   D   E   F
AAAA  10:    1   3   5   7   9   B   C   D   E   F
AAAA   9:    1   3   5   7   9   B   D   E   F
BBBB   8:    1   3   5   7   9   B   D   F
BBBB   7:    3   5   7   9   B   D   F
BBBB   6:    5   7   9   B   D   F
BBBB   5:    7   9   B   D   F
BBBB   4:    9   B   D   F
BBBB   3:    B   D   F
BBBB   2:    D   F
BBBB   1:    F
CCCC- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::delete
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 36;
  $N->for(sub
   {my ($i) = @_;
    $t->put($i, $i);
   });
  $t->delete(K 1 =>  0); $t->printInOrder(" 0");
  $t->delete(K 1 =>  5); $t->printInOrder(" 5");
  $t->delete(K 1 => 10); $t->printInOrder("10");
  $t->delete(K 1 => 15); $t->printInOrder("15");
  $t->delete(K 1 => 20); $t->printInOrder("20");
  $t->delete(K 1 => 25); $t->printInOrder("25");
  $t->delete(K 1 => 30); $t->printInOrder("30");
  $t->delete(K 1 => 35); $t->printInOrder("35");

  $t->delete(K 1 =>  1); $t->printInOrder(" 1");
  $t->delete(K 1 =>  6); $t->printInOrder(" 6");
  $t->delete(K 1 => 11); $t->printInOrder("11");
  $t->delete(K 1 => 16); $t->printInOrder("16");
  $t->delete(K 1 => 21); $t->printInOrder("21");
  $t->delete(K 1 => 26); $t->printInOrder("26");
  $t->delete(K 1 => 31); $t->printInOrder("31");

  $t->delete(K 1 =>  2); $t->printInOrder(" 2");
  $t->delete(K 1 =>  7); $t->printInOrder(" 7");
  $t->delete(K 1 => 12); $t->printInOrder("12");
  $t->delete(K 1 => 17); $t->printInOrder("17");
  $t->delete(K 1 => 22); $t->printInOrder("22");
  $t->delete(K 1 => 27); $t->printInOrder("27");
  $t->delete(K 1 => 32); $t->printInOrder("32");

  $t->delete(K 1 =>  3); $t->printInOrder(" 3");
  $t->delete(K 1 =>  8); $t->printInOrder(" 8");
  $t->delete(K 1 => 13); $t->printInOrder("13");
  $t->delete(K 1 => 18); $t->printInOrder("18");
  $t->delete(K 1 => 23); $t->printInOrder("23");
  $t->delete(K 1 => 28); $t->printInOrder("28");
  $t->delete(K 1 => 33); $t->printInOrder("33");

  $t->delete(K 1 =>  4); $t->printInOrder(" 4");
  $t->delete(K 1 =>  9); $t->printInOrder(" 9");
  $t->delete(K 1 => 14); $t->printInOrder("14");
  $t->delete(K 1 => 19); $t->printInOrder("19");
  $t->delete(K 1 => 24); $t->printInOrder("24");
  $t->delete(K 1 => 29); $t->printInOrder("29");
  $t->delete(K 1 => 34); $t->printInOrder("34");

  ok Assemble eq => <<END, avx512=>1;
 0  35:    1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
 5  34:    1   2   3   4   6   7   8   9   A   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
10  33:    1   2   3   4   6   7   8   9   B   C   D   E   F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
15  32:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
20  31:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  19  1A  1B  1C  1D  1E  1F  20  21  22  23
25  30:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1E  1F  20  21  22  23
30  29:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22  23
35  28:    1   2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
 1  27:    2   3   4   6   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
 6  26:    2   3   4   7   8   9   B   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
11  25:    2   3   4   7   8   9   C   D   E  10  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
16  24:    2   3   4   7   8   9   C   D   E  11  12  13  15  16  17  18  1A  1B  1C  1D  1F  20  21  22
21  23:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1A  1B  1C  1D  1F  20  21  22
26  22:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  1F  20  21  22
31  21:    2   3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
 2  20:    3   4   7   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
 7  19:    3   4   8   9   C   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
12  18:    3   4   8   9   D   E  11  12  13  16  17  18  1B  1C  1D  20  21  22
17  17:    3   4   8   9   D   E  12  13  16  17  18  1B  1C  1D  20  21  22
22  16:    3   4   8   9   D   E  12  13  17  18  1B  1C  1D  20  21  22
27  15:    3   4   8   9   D   E  12  13  17  18  1C  1D  20  21  22
32  14:    3   4   8   9   D   E  12  13  17  18  1C  1D  21  22
 3  13:    4   8   9   D   E  12  13  17  18  1C  1D  21  22
 8  12:    4   9   D   E  12  13  17  18  1C  1D  21  22
13  11:    4   9   E  12  13  17  18  1C  1D  21  22
18  10:    4   9   E  13  17  18  1C  1D  21  22
23   9:    4   9   E  13  18  1C  1D  21  22
28   8:    4   9   E  13  18  1D  21  22
33   7:    4   9   E  13  18  1D  22
 4   6:    9   E  13  18  1D  22
 9   5:    E  13  18  1D  22
14   4:   13  18  1D  22
19   3:   18  1D  22
24   2:   1D  22
29   1:   22
34- empty
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::findNext
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 8;
  $N->for(sub
   {my ($i) = @_;
    $t->put(2*$i, 2*$i);
   });

  (2*$N)->for(sub
   {my ($i) = @_;
    $i->outRightInDec(4); PrintOutString " -> ";
    $t->findNext($i);
    $t->found->out("f: ", " ");
    If $t->found > 0, Then {$t->key->out};
    PrintOutStringNL '.';
   });

  ok Assemble eq => <<END, avx512=>1;
   0 -> f: .... .... .... ...2 key    : .... .... .... ...2.
   1 -> f: .... .... .... ...2 key    : .... .... .... ...2.
   2 -> f: .... .... .... ...4 key    : .... .... .... ...4.
   3 -> f: .... .... .... ...4 key    : .... .... .... ...4.
   4 -> f: .... .... .... ...8 key    : .... .... .... ...6.
   5 -> f: .... .... .... ...8 key    : .... .... .... ...6.
   6 -> f: .... .... .... ..10 key    : .... .... .... ...8.
   7 -> f: .... .... .... ..10 key    : .... .... .... ...8.
   8 -> f: .... .... .... ..20 key    : .... .... .... ...A.
   9 -> f: .... .... .... ..20 key    : .... .... .... ...A.
  10 -> f: .... .... .... ..40 key    : .... .... .... ...C.
  11 -> f: .... .... .... ..40 key    : .... .... .... ...C.
  12 -> f: .... .... .... ..80 key    : .... .... .... ...E.
  13 -> f: .... .... .... ..80 key    : .... .... .... ...E.
  14 -> f: .... .... .... ...0 .
  15 -> f: .... .... .... ...0 .
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::findPrev
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 8;
  $N->for(sub
   {my ($i) = @_;
    $t->put(2*$i, 2*$i);
   });

  (2*$N)->for(sub
   {my ($i) = @_;
    $i->outRightInDec(4); PrintOutString " -> ";
    $t->findPrev($i);
    $t->found->out("f: ", " ");
    If $t->found > 0, Then {$t->key->out};
    PrintOutStringNL '.';
   });

  ok Assemble eq => <<END, avx512=>1;
   0 -> f: .... .... .... ...0 .
   1 -> f: .... .... .... ...1 key    : .... .... .... ...0.
   2 -> f: .... .... .... ...1 key    : .... .... .... ...0.
   3 -> f: .... .... .... ...2 key    : .... .... .... ...2.
   4 -> f: .... .... .... ...2 key    : .... .... .... ...2.
   5 -> f: .... .... .... ...4 key    : .... .... .... ...4.
   6 -> f: .... .... .... ...4 key    : .... .... .... ...4.
   7 -> f: .... .... .... ...8 key    : .... .... .... ...6.
   8 -> f: .... .... .... ...8 key    : .... .... .... ...6.
   9 -> f: .... .... .... ..10 key    : .... .... .... ...8.
  10 -> f: .... .... .... ..10 key    : .... .... .... ...8.
  11 -> f: .... .... .... ..20 key    : .... .... .... ...A.
  12 -> f: .... .... .... ..20 key    : .... .... .... ...A.
  13 -> f: .... .... .... ..40 key    : .... .... .... ...C.
  14 -> f: .... .... .... ..40 key    : .... .... .... ...C.
  15 -> f: .... .... .... ..80 key    : .... .... .... ...E.
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::put #TNasm::X86::Tree::size
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->put(K(key => 1), V(key => 1));  $t->size->outNL;
  $t->put(K(key => 2), K(key => 2));  $t->size->outNL;
  $t->put(V(key => 2), V(key => 2));  $t->size->outNL;

  $t->put(K(key => 3), K(key => 3));  $t->size->outNL;
  $t->put(V(key => 3), V(key => 3));  $t->size->outNL;

  $t->put(K(key => 4), K(key => 4));  $t->size->outNL;
  $t->put(K(key => 4), K(key => 4));  $t->size->outNL;

  $t->put(K(key => 5), K(key => 5));  $t->size->outNL;
  $t->put(V(key => 5), V(key => 5));  $t->size->outNL;

  $t->put(K(key => 6), K(key => 5));  $t->size->outNL;
  $t->put(V(key => 6), V(key => 5));  $t->size->outNL;

  $t->put(K(key => 7), K(key => 7));  $t->size->outNL;
  $t->put(V(key => 7), V(key => 7));  $t->size->outNL;

  $t->put(K(key => 8), K(key => 8));  $t->size->outNL;
  $t->put(V(key => 8), V(key => 8));  $t->size->outNL;

  $t->put(K(key => 9), K(key => 9));  $t->size->outNL;
  $t->put(V(key => 9), V(key => 9));  $t->size->outNL;

  $t->put(K(key => 10), K(key => 10));  $t->size->outNL;
  $t->put(V(key => 10), V(key => 10));  $t->size->outNL;

  $t->put(K(key => 11), K(key => 11));  $t->size->outNL;
  $t->put(V(key => 11), V(key => 11));  $t->size->outNL;

  $t->put(K(key => 12), K(key => 12));  $t->size->outNL;
  $t->put(V(key => 12), V(key => 12));  $t->size->outNL;

  $t->put(K(key => 13), K(key => 13));  $t->size->outNL;
  $t->put(V(key => 13), V(key => 13));  $t->size->outNL;

  $t->put(K(key => 14), K(key => 14));  $t->size->outNL;
  $t->put(V(key => 14), V(key => 14));  $t->size->outNL;

  $t->put(K(key => 15), K(key => 15));  $t->size->outNL;
  $t->put(V(key => 15), V(key => 15));  $t->size->outNL;

 $t->put(K(key => 4), K(key => 4));

 $t->dump8xx("AAA");

  ok Assemble debug => 0, eq => <<END, clocks=>18177;
size of tree: .... .... .... ...1
size of tree: .... .... .... ...2
size of tree: .... .... .... ...2
size of tree: .... .... .... ...3
size of tree: .... .... .... ...3
size of tree: .... .... .... ...4
size of tree: .... .... .... ...4
size of tree: .... .... .... ...5
size of tree: .... .... .... ...5
size of tree: .... .... .... ...6
size of tree: .... .... .... ...6
size of tree: .... .... .... ...7
size of tree: .... .... .... ...7
size of tree: .... .... .... ...8
size of tree: .... .... .... ...8
size of tree: .... .... .... ...9
size of tree: .... .... .... ...9
size of tree: .... .... .... ...A
size of tree: .... .... .... ...A
size of tree: .... .... .... ...B
size of tree: .... .... .... ...B
size of tree: .... .... .... ...C
size of tree: .... .... .... ...C
size of tree: .... .... .... ...D
size of tree: .... .... .... ...D
size of tree: .... .... .... ...E
size of tree: .... .... .... ...E
size of tree: .... .... .... ...F
size of tree: .... .... .... ...F
AAA
Tree: .... .... .... ..40
At:      200                                                                                length:        1,  data:      240,  nodes:      280,  first:       40, root, parent
  Index:        0
  Keys :        7
  Data :        7
  Nodes:       80      140
    At:       80                                                                            length:        6,  data:       C0,  nodes:      100,  first:       40,  up:      200, leaf
      Index:        0        1        2        3        4        5
      Keys :        1        2        3        4        5        6
      Data :        1        2        3        4        5        5
    end
    At:      140                                                                            length:        8,  data:      180,  nodes:      1C0,  first:       40,  up:      200, leaf
      Index:        0        1        2        3        4        5        6        7
      Keys :        8        9        A        B        C        D        E        F
      Data :        8        9        A        B        C        D        E        F
    end
end
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::by
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;
  $N->for(sub
   {my ($i) = @_;
    $t->put($i, 2 * $i);
   });

  $t->by(sub
   {my ($tree, $start, $next, $end) = @_;
    $tree->key->out("");  $tree->data->outNL(" ");
   });

  ok Assemble eq => <<END, avx512=>1;
.... .... .... ...0 .... .... .... ...0
.... .... .... ...1 .... .... .... ...2
.... .... .... ...2 .... .... .... ...4
.... .... .... ...3 .... .... .... ...6
.... .... .... ...4 .... .... .... ...8
.... .... .... ...5 .... .... .... ...A
.... .... .... ...6 .... .... .... ...C
.... .... .... ...7 .... .... .... ...E
.... .... .... ...8 .... .... .... ..10
.... .... .... ...9 .... .... .... ..12
.... .... .... ...A .... .... .... ..14
.... .... .... ...B .... .... .... ..16
.... .... .... ...C .... .... .... ..18
.... .... .... ...D .... .... .... ..1A
.... .... .... ...E .... .... .... ..1C
.... .... .... ...F .... .... .... ..1E
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::yb
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;
  $N->for(sub
   {my ($i) = @_;
    $t->put($i, 2* $i);
   });

  $t->yb(sub
   {my ($tree, $start, $prev, $end) = @_;
    $tree->key->out("");  $tree->data->outNL(" ");
   });

  ok Assemble eq => <<END, avx512=>1;
.... .... .... ...F .... .... .... ..1E
.... .... .... ...E .... .... .... ..1C
.... .... .... ...D .... .... .... ..1A
.... .... .... ...C .... .... .... ..18
.... .... .... ...B .... .... .... ..16
.... .... .... ...A .... .... .... ..14
.... .... .... ...9 .... .... .... ..12
.... .... .... ...8 .... .... .... ..10
.... .... .... ...7 .... .... .... ...E
.... .... .... ...6 .... .... .... ...C
.... .... .... ...5 .... .... .... ...A
.... .... .... ...4 .... .... .... ...8
.... .... .... ...3 .... .... .... ...6
.... .... .... ...2 .... .... .... ...4
.... .... .... ...1 .... .... .... ...2
.... .... .... ...0 .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::push #TNasm::X86::Tree::peek #TNasm::X86::Tree::pop #TNasm::X86::Tree::get
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;
  $N->for(sub
   {my ($i) = @_;
    $t->push($i);
   });

  $t->peek(1)->data ->outNL;
  $t->peek(2)->data ->outNL;
  $t->peek(3)->found->outNL;
  $t->peek(2 * $N    )->found->outNL;

  $t->size->outNL;
  $t->get(8); $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;

  $N->for(sub
   {my ($i) = @_;
    $t->pop; $t->found->out("f: ", " ");  $t->key->out("i: ", " "); $t->data->outNL;
   });
# $t->pop; $t->found->outNL("f: ");

  ok Assemble eq => <<END, clocks => 29_852;
data   : .... .... .... ...F
data   : .... .... .... ...E
found  : .... .... .... ..40
found  : .... .... .... ...0
size of tree: .... .... .... ..10
f: .... .... .... ...2 i: .... .... .... ...8 data   : .... .... .... ...8
f: .... .... .... ...1 i: .... .... .... ...F data   : .... .... .... ...F
f: .... .... .... ...1 i: .... .... .... ...E data   : .... .... .... ...E
f: .... .... .... ...1 i: .... .... .... ...D data   : .... .... .... ...D
f: .... .... .... ...1 i: .... .... .... ...C data   : .... .... .... ...C
f: .... .... .... ...1 i: .... .... .... ...B data   : .... .... .... ...B
f: .... .... .... ...1 i: .... .... .... ...A data   : .... .... .... ...A
f: .... .... .... ...1 i: .... .... .... ...9 data   : .... .... .... ...9
f: .... .... .... ...1 i: .... .... .... ...8 data   : .... .... .... ...8
f: .... .... .... ...1 i: .... .... .... ...7 data   : .... .... .... ...7
f: .... .... .... ...1 i: .... .... .... ...6 data   : .... .... .... ...6
f: .... .... .... ...1 i: .... .... .... ...5 data   : .... .... .... ...5
f: .... .... .... ...1 i: .... .... .... ...4 data   : .... .... .... ...4
f: .... .... .... ...1 i: .... .... .... ...3 data   : .... .... .... ...3
f: .... .... .... ...1 i: .... .... .... ...2 data   : .... .... .... ...2
f: .... .... .... ...1 i: .... .... .... ...1 data   : .... .... .... ...1
f: .... .... .... ...1 i: .... .... .... ...0 data   : .... .... .... ...0
END
 }

test5: goto test6 unless $test{5};


#latest:
if (1) {                                                                        #TNasm::X86::Tree::clear #TNasm::X86::Tree::free #TNasm::X86::Area::freeChainSpace  #TNasm::X86::Area::clear
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $N = K loop => 16;

  $N->for(sub {my ($i) = @_; $t->push($i+1)});
  $t->size->out("t: ", " ");
  $a->used->out("u: ", " ");
  $a->freeChainSpace->out("f: ", " ");
  $a->size->outNL;
  $t->clear;
  $t->size->out("t: ", " ");
  $a->used->out("u: ", " ");
  $a->freeChainSpace->out("f: ", " ");
  $a->size->outNL;

  $N->for(sub {my ($i) = @_; $t->push($i+1)});
  $t->size->out("t: ", " ");
  $a->used->out("u: ", " ");
  $a->freeChainSpace->out("f: ", " ");
  $a->size->outNL;
  $t->clear;
  $t->size->out("t: ", " ");
  $a->used->out("u: ", " ");
  $a->freeChainSpace->out("f: ", " ");
  $a->size->outNL;

  $N->for(sub {my ($i) = @_; $t->push($i+1)});
  $t->free;
  $a->used->out("Clear tree:            u: ");
  $a->freeChainSpace->out(" f: ", " ");
  $a->size->outNL;

  $a->clear;
  $a->used->out("Clear area:            u: ");
  $a->freeChainSpace->out(" f: ", " ");
  $a->size->outNL;

  ok Assemble eq => <<END, label=>'t5';
t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
t: .... .... .... ..10 u: .... .... .... .280 f: .... .... .... ...0 size of area: .... .... .... 10..
t: .... .... .... ...0 u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
Clear tree:            u: .... .... .... .280 f: .... .... .... .240 size of area: .... .... .... 10..
Clear area:            u: .... .... .... ...0 f: .... .... .... ...0 size of area: .... .... .... 10..
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Area::allocZmmBlock #TNasm::X86::Area::freeZmmBlock #TNasm::X86::Area::getZmmBlock #TNasm::X86::Area::putZmmBlock #TNasm::X86::Area::clearZmmBlock #TNasm::X86::Area::dump
  my $a = CreateArea;

  my $m = $a->allocZmmBlock;
  K(K => Rd(1..16))->loadZmm(31);

  $a->putZmmBlock  ($m, 31);
  $a->dump("A");

  $a->getZmmBlock  ($m, 30);
  $a->clearZmmBlock($m);
  $a->getZmmBlock  ($m, 29);

  $a->clearZmmBlock($m);
  PrintOutRegisterInHex 31, 30, 29;

  ok Assemble eq => <<END, avx512=>1;
A
Area     Size:     4096    Used:      128
.... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
 zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm29: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Area::allocZmmBlock #TNasm::X86::Area::freeZmmBlock #TNasm::X86::Area::getZmmBlock #TNasm::X86::Area::putZmmBlock  #TNasm::X86::Area::dump
  my $a = CreateArea;

  K(loop => 3)->for(sub
   {my ($i, $start, $next, $end) = @_;
    $i->outNL;
    my $m1 = $a->allocZmmBlock;
    my $m2 = $a->allocZmmBlock;

    K(K => Rd(1..16))->loadZmm(31);
    K(K => Rd(17..32))->loadZmm(30);
    PrintOutRegisterInHex 31, 30;

    $a->putZmmBlock($m1, 31);
    $a->putZmmBlock($m2, 30);
    $a->dump("A");

    $a->getZmmBlock($m1, 30);
    $a->getZmmBlock($m2, 31);
    PrintOutRegisterInHex 31, 30;

    $a->clearZmmBlock($m1);
    $a->freeZmmBlock($m1);
    $a->dump("B");

    $a->freeZmmBlock($m2);
    $a->dump("C");
   });

  ok Assemble eq => <<END, avx512=>1;
index: .... .... .... ...0
 zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
A
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
.... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
 zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
B
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
C
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
index: .... .... .... ...1
 zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
A
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..80 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
 zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
B
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
C
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
index: .... .... .... ...2
 zmm31: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
 zmm30: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
A
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | .1__ ____ .2__ ____  .3__ ____ .4__ ____  .5__ ____ .6__ ____  .7__ ____ .8__ ____  .9__ ____ .A__ ____  .B__ ____ .C__ ____  .D__ ____ .E__ ____  .F__ ____ 10__ ____
.... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
 zmm31: .... ..20 .... ..1F  .... ..1E .... ..1D - .... ..1C .... ..1B  .... ..1A .... ..19 + .... ..18 .... ..17  .... ..16 .... ..15 - .... ..14 .... ..13  .... ..12 .... ..11
 zmm30: .... ..10 .... ...F  .... ...E .... ...D - .... ...C .... ...B  .... ...A .... ...9 + .... ...8 .... ...7  .... ...6 .... ...5 - .... ...4 .... ...3  .... ...2 .... ...1
B
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 11__ ____ 12__ ____  13__ ____ 14__ ____  15__ ____ 16__ ____  17__ ____ 18__ ____  19__ ____ 1A__ ____  1B__ ____ 1C__ ____  1D__ ____ 1E__ ____  1F__ ____ 20__ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
C
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:
if (1) {                                                                        #Tconvert_rax_from_utf32_to_utf8
# $ 	U+0024                 010 0100                00100100                     24
# £ 	U+00A3 	          000 1010 0011                11000010 10100011            C2 A3
# ह 	  U+0939    	0000 1001 0011 1001                11100000 10100100 10111001   E0 A4 B9
# € 	U+20AC    	0010 0000 1010 1100                11100010 10000010 10101100   E2 82 AC
# 한 	U+D55C     	1101 0101 0101 1100                11101101 10010101 10011100   ED 95 9C
# 𐍈   	U+10348 	0 0001 0000 0011 0100 1000 	11110000 10010000 10001101 10001000   F0 90 8D 88
  Mov rax, 0x40;                                                                # 0x40
  convert_rax_from_utf32_to_utf8;
  PrintOutRegisterInHex rax;

  Mov rax, 0x03b1;                                                              # 0xCE 0xB1
  convert_rax_from_utf32_to_utf8;
  PrintOutRegisterInHex rax;

  Mov rax, 0x20ac;                                                              # 0xE2 0x82 0xAC;
  convert_rax_from_utf32_to_utf8;
  PrintOutRegisterInHex rax;

  Mov rax, 0x10348;                                                             # 0xf0 0x90 0x8d 0x88
  convert_rax_from_utf32_to_utf8;
  PrintOutRegisterInHex rax;

  ok Assemble eq => <<END, avx512=>1;
   rax: .... .... .... ..40
   rax: .... .... .... B1CE
   rax: .... .... ..AC 82E2
   rax: .... .... 888D 90F0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::outAsUtf8 #TNasm::X86::Tree::append #TNasm::X86::Tree::reverse #TNasm::X86::Tree::substring
  my $a = CreateArea;
  my $t = $a->CreateTree;

  $t->push(K alpha => 0x03b1);
  $t->push(K beta  => 0x03b2);
  $t->push(K gamma => 0x03b3);
  $t->push(K delta => 0x03b4);

  $t->outAsUtf8NL;

  $t->append($t);
  $t->outAsUtf8NL;

  $t->append($t);
  $t->outAsUtf8NL;

  my $T = $t->substring(K(key => 4), K(key => 8));
  $T->outAsUtf8NL;

  my $r = $T->reverse;
  $r->outAsUtf8NL;

  ok Assemble eq => <<END, avx512=>1;
αβγδ
αβγδαβγδ
αβγδαβγδαβγδαβγδ
αβγδ
δγβα
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::cloneDescriptor #TNasm::X86::Tree::copyDescriptor
  my $a = CreateArea;
  my $t = $a->CreateTree;
  $t->push(ord 'a');
  my $T = $t->cloneDescriptor;
  $T->push(ord 'b');
  my $c = $a->CreateTree;
  $c->copyDescriptor($T);
  $T->push(ord 'c');

  $t->outAsUtf8NL;
  ok Assemble eq => <<END, avx512=>1;
abc
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::appendAscii #TNasm::X86::Tree::append #TNasm::X86::Tree::outAsUtf8NL #TNasm::X86::Tree::clone
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $b = Rb(0x41..0x51);
  $t->appendAscii(K(address=> $b), K(size => 1));
  $t->outAsUtf8NL;
  my $T = $a->CreateTree;
  $T->append($t);
  $T->append($t);
  $T->outAsUtf8NL;
  $T->appendAscii(K(address=> $b) + 1, K(size => 1));
  my $c = $T->clone;
  $c->outAsUtf8NL;
  my $d = $c->substring(1, 3);
  $d->outAsUtf8NL;
  ok Assemble eq => <<END, avx512=>1;
A
AA
AAB
AB
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::push
  my $b = Rb(0x41..0x51);
  my $a = CreateArea;
  my $T;
  for my $i(1..8)
   {my $t = $a->CreateTree;
    $t->appendAscii(K(address=> $b), K(size => 1));
    $t->push($T) if $T;
    $T = $t;
   }

  $T->dump8xx("T");
  ok Assemble eq => <<END, trace=>0;
T
Tree: .... .... .... .740
At:      780                                                                                length:        2,  data:      7C0,  nodes:      800,  first:      740, root, leaf,  trees:            10
  Index:        0        1
  Keys :        0        1
  Data :       41      64*
   Tree:      640
     At:      680                                                                           length:        2,  data:      6C0,  nodes:      700,  first:      640, root, leaf,  trees:            10
       Index:        0        1
       Keys :        0        1
       Data :       41      54*
        Tree:      540
          At:      580                                                                      length:        2,  data:      5C0,  nodes:      600,  first:      540, root, leaf,  trees:            10
            Index:        0        1
            Keys :        0        1
            Data :       41      44*
             Tree:      440
               At:      480                                                                 length:        2,  data:      4C0,  nodes:      500,  first:      440, root, leaf,  trees:            10
                 Index:        0        1
                 Keys :        0        1
                 Data :       41      34*
                  Tree:      340
                    At:      380                                                            length:        2,  data:      3C0,  nodes:      400,  first:      340, root, leaf,  trees:            10
                      Index:        0        1
                      Keys :        0        1
                      Data :       41      24*
                       Tree:      240
                         At:      280                                                       length:        2,  data:      2C0,  nodes:      300,  first:      240, root, leaf,  trees:            10
                           Index:        0        1
                           Keys :        0        1
                           Data :       41      14*
                            Tree:      140
                              At:      180                                                  length:        2,  data:      1C0,  nodes:      200,  first:      140, root, leaf,  trees:            10
                                Index:        0        1
                                Keys :        0        1
                                Data :       41       4*
                                 Tree:       40
                                   At:       80                                             length:        1,  data:       C0,  nodes:      100,  first:       40, root, leaf
                                     Index:        0
                                     Keys :        0
                                     Data :       41
                                   end
                              end
                         end
                    end
               end
          end
     end
end
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::peekSubTree
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;

  $T->push(0x22);
  $t->push($T);
  my $u = $t->peekSubTree(1);
  $u->peek(1);
  $u->found->outNL;
  $u->data->outNL;
  ok Assemble eq => <<END, avx512=>1;
found  : .... .... .... ...1
data   : .... .... .... ..22
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::dump8xx
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;

  $T->push(K key => 1);
  $t->push($T);
  $t->dump8xx('AA');
  my $s = $t->popSubTree;
  $t->dump8xx('BB');
  $s->dump8xx('CC');
  ok Assemble eq => <<END, avx512=>1;
AA
Tree: .... .... .... ..40
At:      180                                                                                length:        1,  data:      1C0,  nodes:      200,  first:       40, root, leaf,  trees:             1
  Index:        0
  Keys :        0
  Data :       8*
   Tree:       80
     At:       C0                                                                           length:        1,  data:      100,  nodes:      140,  first:       80, root, leaf
       Index:        0
       Keys :        0
       Data :        1
     end
end
BB
- empty
CC
Tree: .... .... .... ..80
At:       C0                                                                                length:        1,  data:      100,  nodes:      140,  first:       80, root, leaf
  Index:        0
  Keys :        0
  Data :        1
end
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::union #TNasm::X86::Tree::intersection
  my $a = CreateArea;
  my $r = $a->CreateTree;
  my $s = $a->CreateTree;
  my $t = $a->CreateTree;

  $r->put(K(key => 1), K(data => 1));
  $r->put(K(key => 2), K(data => 2));

  $s->put(K(key => 1), K(data => 1));
  $s->put(K(key => 3), K(data => 3));

  $t->push($r);
  $t->push($s);

  my $u = $t->union;
  $t->dump('input 1 2  1 3');
  $u->dump('union 1 2    3');

  my $i = $t->intersection;
  $i->dump('intersection 1');

  ok Assemble eq => <<END, avx512=>1;
input 1 2  1 3
At:  280                    length:    2,  data:  2C0,  nodes:  300,  first:   C0, root, leaf,  trees:            11
  Index:    0    1
  Keys :    0    1
  Data :  10*  1C*
     At:  100               length:    2,  data:  140,  nodes:  180,  first:   40, root, leaf
       Index:    0    1
       Keys :    1    2
       Data :    1    2
     end
     At:  1C0               length:    2,  data:  200,  nodes:  240,  first:   80, root, leaf
       Index:    0    1
       Keys :    1    3
       Data :    1    3
     end
end
union 1 2    3
At:  380                    length:    3,  data:  3C0,  nodes:  400,  first:  340, root, leaf
  Index:    0    1    2
  Keys :    1    2    3
  Data :    1    2    3
end
intersection 1
At:  480                    length:    1,  data:  4C0,  nodes:  500,  first:  440, root, leaf
  Index:    0
  Keys :    1
  Data :    1
end
END
 }

#latest:
if (1) {
  my $t = Subroutine                                                            #TSubroutine
   {my ($p, $s, $sub) = @_;
    PrintOutStringNL "TTTT";
    $$p{p}->outNL;
   } parameters=>[qw(p)], name=>"t";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;
    PrintOutStringNL "SSS";
    $$p{p}->outNL;
   } parameters=>[qw(p)], name=>"s";

  $s->call(parameters => {p => V(p => 1)});
  $t->call(parameters => {p => V(p => 2)});

  $s->call(parameters => {p => V(p => 3)}, override=>K call => $t->start);
  $s->call(parameters => {p => V(p => 4)}, override=>K call => $s->start);
  $s->call(parameters => {p => V(p => 5)});

  ok Assemble eq => <<END, avx512=>1;
SSS
p: .... .... .... ...1
TTTT
p: .... .... .... ...2
TTTT
p: .... .... .... ...3
SSS
p: .... .... .... ...4
SSS
p: .... .... .... ...5
END
 }

#latest:
if (1) {
  my $a = V(a => 0);
  my $b = $a;
  $a->outNL;
  $b++;
  $a->outNL;
  $b--;
  $a->outNL;
  ok Assemble eq => <<END;
a: .... .... .... ...0
a: .... .... .... ...1
a: .... .... .... ...0
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Variable::dereference #TNasm::X86::Variable::address #TNasm::X86::Variable::update
          V(a => 2);
          V(a => 1);
  my $a = V(a => 0)->address;
  ($a+ 0)->dereference->outNL;
  ($a+ 8)->dereference->outNL;
  ($a+16)->dereference->outNL;
  ($a+16)->update(K key => 3);
  ($a+16)->dereference->outNL;
  ok Assemble eq => <<END;
deref (addr a add 0): .... .... .... ...0
deref (addr a add 8): .... .... .... ...1
deref (addr a add 16): .... .... .... ...2
deref (addr a add 16): .... .... .... ...3
END
 }

test6: goto test7 unless $test{6};

#latest:
if (1) {                                                                        #TifOr
  my $a = K key => 1;
  my $b = K key => 1;

  ifOr [sub{$a==$a}, sub{$a==$a}],
  Then
   {PrintOutStringNL "AAAA11";
   },
  Else
   {PrintOutStringNL "AAAA22";
   };

  ifOr [sub{$a==$a}, sub{$a!=$a}],
  Then
   {PrintOutStringNL "BBBB11";
   },
   Else
   {PrintOutStringNL "BBBB22";
   };

  ifOr [sub{$a!=$a}, sub{$a==$a}],
  Then
   {PrintOutStringNL "CCCC11";
   },
   Else
   {PrintOutStringNL "CCCC22";
   };

  ifOr [sub{$a!=$b}, sub{$a!=$b}],
  Then
   {PrintOutStringNL "DDDD11";
   },
   Else
   {PrintOutStringNL "DDDD22";
   };

  ok Assemble eq => <<END, avx512=>1;
AAAA11
BBBB11
CCCC11
DDDD22
END
 }

#latest:                                                                        #TifAnd
if (1) {
  my $a = K key => 1;
  my $b = K key => 1;

  ifAnd [sub{$a==$a}, sub{$a==$a}],
  Then
   {PrintOutStringNL "AAAA11";
   },
  Else
   {PrintOutStringNL "AAAA22";
   };

  ifAnd [sub{$a==$a}, sub{$a!=$a}],
  Then
   {PrintOutStringNL "BBBB11";
   },
   Else
   {PrintOutStringNL "BBBB22";
   };

  ifAnd [sub{$a!=$a}, sub{$a==$a}],
  Then
   {PrintOutStringNL "CCCC11";
   },
   Else
   {PrintOutStringNL "CCCC22";
   };

  ifAnd [sub{$a!=$b}, sub{$a!=$b}],
  Then
   {PrintOutStringNL "DDDD11";
   },
   Else
   {PrintOutStringNL "DDDD22";
   };

  ok Assemble eq => <<END, avx512=>1;
AAAA11
BBBB22
CCCC22
DDDD22
END
 }

#latest:                                                                        #TBlock
if (1) {
  Block
   {my ($end, $start) = @_;
    PrintOutStringNL "AAAA";
    Jmp $end;
    PrintOutStringNL "BBBB";
   };
  ok Assemble eq => <<END, avx512=>1;
AAAA
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Unisyn::Lex::AlphabetsArray
  my ($N, $A) = Nasm::X86::Unisyn::Lex::AlphabetsArray;
  Mov rax, "[$N]";
  PrintOutRegisterInHex rax;
  Mov rax, $A;
  Add rax, ord('𝝰');
  Mov al, "[rax]";
  And rax, 0xff;
  PrintOutRegisterInHex rax;

  Mov rax, $A;
  Add rax, ord('𝔸');
  Mov al, "[rax]";
  And rax, 0xff;
  PrintOutRegisterInHex rax;

  my sub expect($)
   {my ($n) = @_;                                                               # Parameters
    $n >= 0x10 ? sprintf("%2X", $n) : sprintf(".%1X", $n)
   }

  my $a = expect Nasm::X86::Unisyn::Lex::Number::v;
  my $b = expect Nasm::X86::Unisyn::Lex::Number::d;
  ok Assemble eq => <<END, avx512=>1;
   rax: .... .... ...1 EEF2
   rax: .... .... .... ..$a
   rax: .... .... .... ..$b
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Unisyn::Lex::PermissibleTransitionsArray
  my $a = Nasm::X86::Unisyn::Lex::Number::a;                                    # Assign-2 - right to left
  my $b = Nasm::X86::Unisyn::Lex::Number::b;                                    # Open

  my ($N, $A) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;
  Mov rax, $A;
  Add rax, ($a << Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits) + $a;
  Mov al, "[rax]";
  And rax, 0xff;
  PrintOutRegisterInHex rax;

  Mov rax, $A;
  Add rax, ($b << Nasm::X86::Unisyn::Lex::PermissibleTransitionsArrayBits) + $b;
  Mov al, "[rax]";
  And rax, 0xff;
  PrintOutRegisterInHex rax;
  ok Assemble eq => <<END, avx512=>1;
   rax: .... .... .... ..FF
   rax: .... .... .... ...1
END
 }

#latest:
if (1) {                                                                        # Confirm that closing brackets are always one more than opening brackets
  my @b = Nasm::X86::Unisyn::Lex::Letter::b;                                    # Open
  my @B = Nasm::X86::Unisyn::Lex::Letter::B;                                    # Close

  confess unless @b == @B;
  for my $i(keys @b)
   {confess "Mismatch at $i " unless $b[$i] + 1 == $B[$i];
   }
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Unisyn::Parse::dumpPostOrder
  my $u = Nasm::X86::Unisyn::Lex::composeUnisyn("va w m+ vb");
  my $t = '𝗮 ＋𝗯';
  is_deeply $u, $t;

  my $p = &ParseUnisyn(constantString $t);                                      # Parse the utf8 string minus the final new line

  $p->dumpPostOrder;
  ok Assemble eq <<END;
._𝗮 
._𝗯
＋
END
 }

sub testParseUnisyn($$$)                                                        #P Test the parse of a unisyn expression.
 {my ($compose, $text, $parse) = @_;                                            # The composing expression used to create a unisyn expression, the expected composed expression, the expected parse tree

  my $u = Nasm::X86::Unisyn::Lex::composeUnisyn($compose);

  is_deeply $u, $text;

  my $p = &ParseUnisyn(constantString $text);                                   # Parse the utf8 string minus the final new line

  $p->dump;
  Assemble;

  if (-e $programOut)                                                           # Print parse tree
   {my $r = readFile $programOut;
       $r =~ s(\n) ()gs;                                                        # Collapse result
    if ($r ne $parse)
     {say STDERR $text;
      say STDERR $r;
      say STDERR "Want: ", dump($parse);
      say STDERR "Got : ", dump($r);
      confess "Parse failed" unless onGitHub;
     }
   }
  else
   {confess "No result file: $programOut";
   }
 }

test7: goto test8 unless $test{7};

#latest:;
testParseUnisyn 'b( vaaaa  w2 m+ w2 vbbbb B)', "【𝗮𝗮𝗮𝗮  ＋  𝗯𝗯𝗯𝗯】", q(【._＋  ._._𝗮𝗮𝗮𝗮  ._._𝗯𝗯𝗯𝗯);

testParseUnisyn 'b( va m+ vb B)', "【𝗮＋𝗯】", q(【._＋._._𝗮._._𝗯);

testParseUnisyn 'va m+ vb m+ vc', "𝗮＋𝗯＋𝗰",  q(＋._＋._._𝗮._._𝗯._𝗰);

#latest: ;
testParseUnisyn 'b( A1 m+ va m* vb m+ vc m+ A2 m* va m+ vb m+ vc B)',
                 "【1＋𝗮✕𝗯＋𝗰＋2✕𝗮＋𝗯＋𝗰】",
                 q(【._＋._._＋._._._✕._._._._＋._._._._._＋._._._._._._✕._._._._._._._＋._._._._._._._._1._._._._._._._._𝗮._._._._._._._𝗯._._._._._._𝗰._._._._._2._._._._𝗮._._._𝗯._._𝗰);

testParseUnisyn 'pl va',          "𝒍𝗮", q(𝒍._𝗮);
testParseUnisyn 'pL va',          "𝑳𝗮", q(𝑳._𝗮);
testParseUnisyn 'va qK',          "𝗮𝙆", q(𝙆._𝗮);
testParseUnisyn 'pL va qK',       "𝑳𝗮𝙆", q(𝙆._𝑳._._𝗮);
testParseUnisyn 'b( B) qK',       "【】𝙆", q(𝙆._【);
testParseUnisyn 'pL b( B) qK',    "𝑳【】𝙆", q(𝙆._𝑳._._【);

testParseUnisyn 'va a= b( vb m+ vc B) m* vd lIF ve',
                '𝗮＝【𝗯＋𝗰】✕𝗱𝐈𝐅𝗲',
                q(＝._𝗮._𝐈𝐅._._✕._._._【._._._._＋._._._._._𝗯._._._._._𝗰._._._𝗱._._𝗲);

testParseUnisyn 'va a= vb lIF vc m* vd s va a= vb lIF  vc m* vd s',
                "𝗮＝𝗯𝐈𝐅𝗰✕𝗱⟢𝗮＝𝗯𝐈𝐅𝗰✕𝗱⟢",
                 q(⟢._＝._._𝗮._._𝐈𝐅._._._𝗯._._._✕._._._._𝗰._._._._𝗱._＝._._𝗮._._𝐈𝐅._._._𝗯._._._✕._._._._𝗰._._._._𝗱);

#latest:;
testParseUnisyn '',                                        "",                  qq();
testParseUnisyn 'va',                                      "𝗮",                 qq(𝗮);
testParseUnisyn 'va a= va',                                "𝗮＝𝗮",               qq(＝._𝗮._𝗮);
testParseUnisyn 'va m+ vb',                                "𝗮＋𝗯",               qq(＋._𝗮._𝗯);
testParseUnisyn 'va a= vb m+ vc',                          "𝗮＝𝗯＋𝗰",             qq(＝._𝗮._＋._._𝗯._._𝗰);
testParseUnisyn 'va a= vb m* vc',                          "𝗮＝𝗯✕𝗰",             qq(＝._𝗮._✕._._𝗯._._𝗰);
testParseUnisyn 'b( B)',                                   "【】",                qq(【);
testParseUnisyn 'b( b[ B] B)',                             "【⟦⟧】",              qq(【._⟦);
testParseUnisyn 'b( b[ b< B> B] B)',                       "【⟦⟨⟩⟧】",            qq(【._⟦._._⟨);

test8: goto test9 unless $test{8};

#latest:;
testParseUnisyn 'b( va B)',                                "【𝗮】",               qq(【._𝗮);
testParseUnisyn 'b( b[ va B] B)',                          "【⟦𝗮⟧】",             qq(【._⟦._._𝗮);
testParseUnisyn 'b( b[ va m+ vb B] B)',                    "【⟦𝗮＋𝗯⟧】",           qq(【._⟦._._＋._._._𝗮._._._𝗯);
testParseUnisyn 'b( b[ va m+ vb B] m* b[ va m+ vb B] B)',  "【⟦𝗮＋𝗯⟧✕⟦𝗮＋𝗯⟧】",     qq(【._✕._._⟦._._._＋._._._._𝗮._._._._𝗯._._⟦._._._＋._._._._𝗮._._._._𝗯);
testParseUnisyn 's s s s s',                               "⟢⟢⟢⟢⟢",             qq();
testParseUnisyn 'va s vb',                                 "𝗮⟢𝗯",               qq(⟢._𝗮._𝗯);
testParseUnisyn 'va s s vb',                               "𝗮⟢⟢𝗯",              qq(⟢._𝗮._𝗯);
testParseUnisyn 's s va s s vb s s',                       "⟢⟢𝗮⟢⟢𝗯⟢⟢",          qq(⟢._𝗮._𝗯);
testParseUnisyn 'va a= vb a= vc',                          "𝗮＝𝗯＝𝗰",             qq(＝._𝗮._＝._._𝗯._._𝗰);

test9: goto test10 unless $test{9};

testParseUnisyn 'va a= vb m+ vc a= vd m+ ve',              "𝗮＝𝗯＋𝗰＝𝗱＋𝗲",         qq(＝._𝗮._＝._._＋._._._𝗯._._._𝗰._._＋._._._𝗱._._._𝗲);
testParseUnisyn 'va a= vb m+ vc s vd a= ve m+ vf',         "𝗮＝𝗯＋𝗰⟢𝗱＝𝗲＋𝗳",       qq(⟢._＝._._𝗮._._＋._._._𝗯._._._𝗰._＝._._𝗱._._＋._._._𝗲._._._𝗳);
testParseUnisyn 'va lIF vb',                               "𝗮𝐈𝐅𝗯",              qq(𝐈𝐅._𝗮._𝗯);
testParseUnisyn 'va lIF vb lELSE vc',                      "𝗮𝐈𝐅𝗯𝐄𝐋𝐒𝐄𝗰",         qq(𝐄𝐋𝐒𝐄._𝐈𝐅._._𝗮._._𝗯._𝗰);
testParseUnisyn 'va a= b1 vb m+ vc B1 m* vd lIF ve',       "𝗮＝⌊𝗯＋𝗰⌋✕𝗱𝐈𝐅𝗲",      qq(＝._𝗮._𝐈𝐅._._✕._._._⌊._._._._＋._._._._._𝗯._._._._._𝗰._._._𝗱._._𝗲);
testParseUnisyn 'va a= vb lIF vc m* vd s va a= vb lIF  vc m* vd s', "𝗮＝𝗯𝐈𝐅𝗰✕𝗱⟢𝗮＝𝗯𝐈𝐅𝗰✕𝗱⟢",  qq(⟢._＝._._𝗮._._𝐈𝐅._._._𝗯._._._✕._._._._𝗰._._._._𝗱._＝._._𝗮._._𝐈𝐅._._._𝗯._._._✕._._._._𝗰._._._._𝗱);

#latest:;
testParseUnisyn 'p11 va',                                  "𝑳𝗮",                qq(𝑳._𝗮);
testParseUnisyn 'va q11',                                  "𝗮𝙇",                qq(𝙇._𝗮);

test10: goto test11 unless $test{10};

#latest:
testParseUnisyn 'p11 va q10',                              "𝑳𝗮𝙆",               qq(𝙆._𝑳._._𝗮);
testParseUnisyn 'p11 b( B) q10',                           "𝑳【】𝙆",              qq(𝙆._𝑳._._【);
testParseUnisyn 'p21 b( va m* vb B) q22',                  "𝑽【𝗮✕𝗯】𝙒",           qq(𝙒._𝑽._._【._._._✕._._._._𝗮._._._._𝗯);
testParseUnisyn 'va m+ vb q11',                            "𝗮＋𝗯𝙇",              qq(＋._𝗮._𝙇._._𝗯);
testParseUnisyn 'va m+ p11 vb q11',                        "𝗮＋𝑳𝗯𝙇",             qq(＋._𝗮._𝙇._._𝑳._._._𝗯);
testParseUnisyn 'va m+ p11 vb q11 m+ p21 b( va m* vb B) q22',  "𝗮＋𝑳𝗯𝙇＋𝑽【𝗮✕𝗯】𝙒", qq(＋._＋._._𝗮._._𝙇._._._𝑳._._._._𝗯._𝙒._._𝑽._._._【._._._._✕._._._._._𝗮._._._._._𝗯);
testParseUnisyn 'va m+ p11 vb q11 lIF p21 b( vc m* vd B) q22 lELSE ve m* vf',
            "𝗮＋𝑳𝗯𝙇𝐈𝐅𝑽【𝗰✕𝗱】𝙒𝐄𝐋𝐒𝐄𝗲✕𝗳",                                            qq(𝐄𝐋𝐒𝐄._𝐈𝐅._._＋._._._𝗮._._._𝙇._._._._𝑳._._._._._𝗯._._𝙒._._._𝑽._._._._【._._._._._✕._._._._._._𝗰._._._._._._𝗱._✕._._𝗲._._𝗳);

test11: goto test12 unless $test{11};

#latest:;
testParseUnisyn 'va land vb',                              '𝗮𝐚𝐧𝐝𝗯',             qq(𝐚𝐧𝐝._𝗮._𝗯);

#latest:
testParseUnisyn 'va w a= w vb w  dand w vc w a= w vd', "𝗮 ＝ 𝗯 𝕒𝕟𝕕 𝗰 ＝ 𝗱", q(𝕒𝕟𝕕 ._＝ ._._𝗮 ._._𝗯 ._＝ ._._𝗰 ._._𝗱);

#latest:
if (1)
 {my $s = chr(0x205F).chr(0x205F);
  my $p = &ParseUnisyn(constantString substr <<END, 0, -1);
𝗮${s}＝${s}𝗯${s}𝕒𝕟𝕕${s}𝗰 ${s}＝${s}𝗱
END

  $p->dump;
  ok Assemble eq => <<END, clocks=>16_439;
𝕒𝕟𝕕  
._＝  
._._𝗮  
._._𝗯  
._＝  
._._𝗰   
._._𝗱
END
 }

#latest:
if (1) {                                                                        #TTraceMode
  $TraceMode = 1;
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, "[rax]";
  Mov rax, "[rax]";
  Mov rax, "[rax]";
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);
  Mov rax, Rq(0x22);

  PrintOutRegisterInHex rax;
  eval {Assemble trace=>1};
  ok readFile($traceBack) =~ m(TraceBack start:)s;
 }

#latest:
if (1) {                                                                        #TTraceMode
  $TraceMode = 1;                                                               # Enabling tracing

  my $S = Subroutine                                                            # Load and print rax
   {my ($p, $s, $sub) = @_;
    Mov rax, "[rax]";
    PrintOutRegisterInHex rax;
    If $$p{fail} > 0,                                                           # Fail if requested
    Then
     {Mov rax, "[rax]";
     };
   } name => "s", parameters=>[qw(fail)], trace=>1;

  my $T = Subroutine                                                            # Fail
   {my ($p, $s, $sub) = @_;
    Mov rax, Rq(0x22);
    $S->call(parameters => {fail => K(zero=> 1)});
   } name => "t";


  Mov rax, Rq(0x11);
  $S->call(parameters => {fail => K(zero=> 0)});                                # Call subroutine to be traced
  $T->call;

  Assemble eq=><<END;
   rax: .... .... .... ..11
   rax: .... .... .... ..22
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Subroutine::inline
  my $s = Subroutine                                                            # Load and print rax
   {my ($p, $s, $sub) = @_;
    $$p{ppp}->outNL;
   } name => "s", parameters=>[qw(ppp)];

  $s->call  (parameters => {ppp => V ppp => 0x99});                             # Call   378
  $s->inline(parameters => {ppp => V ppp => 0xaa});                             # Inline 364

  Assemble eq=><<END, avx512=>1;
ppp: .... .... .... ..99
ppp: .... .... .... ..AA
END
 }

ok compactRangeIntoHex(qw(0x1 0x2 0x3 0x5 0x7 0x8 0x9 0xb)) eq                  #TcompactRangeIntoHex
                       "0x1..0x3, 0x5, 0x7..0x9, 0xb";

#latest:
if (1)
 {my $a = CreateArea;
  my $t = $a->CreateTree;
  K(key => 100)->for(sub{my ($i) = @_; $t->put($i, $i)});
  $t->size->outNL;
  ok Assemble eq =><<END, avx512=>1;
size of tree: .... .... .... ..64
END
 }

#latest:;
if (1)
 {my $a = K(key => 11);
  my $b = K(key => 22);

  If $a > $b,
  Then
   {PrintOutStringNL "AAAA biggest";
   },
  Else
   {PrintOutStringNL "BBBB biggest";
   };

  If $a < $b,
  Then
   {PrintOutStringNL "AAAA smallest";
   },
  Else
   {PrintOutStringNL "BBBB smallest";
   };

  If $a != $b,
  Then
   {PrintOutStringNL "AAAA not equal to BBBB by not equal";
   },
  Else
   {PrintOutStringNL "AAAA equals to BBBB by not equal";
   };

  If $a == $b,
  Then
   {PrintOutStringNL "AAAA equal to BBBB";
   },
  Else
   {PrintOutStringNL "AAAA not equal to BBBB";
   };

  If $a == 11,
  Then
   {PrintOutStringNL "AAAA equal 11";
   },
  Else
   {PrintOutStringNL "AAAA not equal 11";
   };

  If $a == 0,
  Then
   {PrintOutStringNL "AAAA equal zero";
   },
  Else
   {PrintOutStringNL "AAAA not equal zero";
   };

  If $a > 0,
  Then
   {PrintOutStringNL "AAAA greater than zero by gt";
   },
  Else
   {PrintOutStringNL "AAAA equal zero by gt";
   };

  my $c = K(key => 0);
  If $c > 0,
  Then
   {PrintOutStringNL "CCCC greater than zero by gt";
   },
  Else
   {PrintOutStringNL "CCCC equal zero by gt";
   };

  If $c == 0,
  Then
   {PrintOutStringNL "CCCC equal zero";
   },
  Else
   {PrintOutStringNL "CCCC not equal zero";
   };


  ok Assemble eq => <<END, avx512=>1;
BBBB biggest
AAAA smallest
AAAA not equal to BBBB by not equal
AAAA not equal to BBBB
AAAA equal 11
AAAA not equal zero
AAAA greater than zero by gt
CCCC equal zero by gt
CCCC equal zero
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::put #TNasm::X86::Tree::find
  my $a = CreateArea;
  my $t = $a->CreateTree;

  my $N = K(key => 999);
  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    $t->put($index, $index);
   });

  $N->for(sub
   {my ($index, $start, $next, $end) = @_;
    $t->find($index);
   });

  ok Assemble eq => <<END, clocks=>763430;
END
 }

#latest:;
if (1) {                                                                        # Create and use a library demonstrating that we can place the containing subroutine in an area, reload that area and call both the containing and the contained subroutines. #TloadAreaIntoAssembly
  unlink my $f = q(zzzArea.data);
  my $sub = "abcd";

  my $s = Subroutine
   {my ($p, $s, $sub) = @_;

    my $a = Subroutine
     {my ($p, $s, $sub) = @_;
      Mov rax, 0x111;
      PrintOutRegisterInHex rax;
     } name => 'a', parameters=>[qw(a)];

    my $b = Subroutine
     {my ($p, $s, $sub) = @_;
      $$p{a}->setReg(rax);
      PrintOutRegisterInHex rax;
     } name => 'b', parameters=>[qw(a)];

    PrintOutStringNL "abcd";
    $$p{a}->outNL;
   } name => $sub, parameters=>[qw(a)], export => $f;                           # Export the library

  my $t = Subroutine {} name => "t", parameters=>[qw(a)];

  my sub mapSubroutines                                                         # Create a string tree mapping subroutine names to subroutine numbers
   {my $n = CreateArea->CreateTree(stringTree=>1);
    $n->putKeyString(constantString("abcd"), K offset => 0);
    $n->putKeyString(constantString("a"),    K offset => 1);
    $n->putKeyString(constantString("b"),    K offset => 2);
    $n
   }

  if (1)                                                                        # Read a library and call the subroutines there-in
   {my $a = ReadArea $f;                                                        # Reload the area elsewhere
    my ($inter, $symbols) = $a->readLibraryHeader(mapSubroutines);              # Create a tree mapping the subroutine numbers to subroutine offsets

    $inter->find(K sub => 1);                                                   # Look up the offset of the first subroutine

    If $inter->found > 0,
    Then
     {$t->call(parameters=>{a => K key => 0x9999},                              # Call position independent code
                            override => $a->address + $inter->data);
     },
    Else
     {PrintOutStringNL "Unable to locate subroutine 'a'";
     };

    $inter->find(K sub => 2);                                                   # Look  up the offset of the second subroutine

    If $inter->found > 0,
    Then
     {$t->call(parameters=>{a        => K key => 0x9999},                       # Call position independent code
                            override => $a->address + $inter->data);
     },
    Else
     {PrintOutStringNL "Unable to locate subroutine 'b'";
     };
   }

  ok Assemble eq=><<END, avx512=>1;
   rax: .... .... .... .111
   rax: .... .... .... 9999
END
  ok -e $f;                                                                     # Confirm we have created the library

  if (1)                                                                        # Include a library in a program
   {my $a = loadAreaIntoAssembly $f;                                            # Load the library from the file it was exported to
    my ($inter, $subroutines) = $a->readLibraryHeader(mapSubroutines);          # Create a tree mapping the subroutine numbers to subroutine offsets

    $inter->find(K sub => 0);                                                   # Look  up the offset of the containing subroutine
    $t->call(parameters=>{a        => K key => 0x6666},                         # Call position independent code
                          override => $a->address + $inter->data);

    $inter->find(K sub => 1);                                                   # Look  up the offset of the first subroutine
    $t->call(parameters=>{a        => K key => 0x7777},                         # Call position independent code
                          override => $a->address + $inter->data);

    $inter->find(K sub => 2);                                                   # Look  up the offset of the second subroutine
    $t->call(parameters=>{a        => K key => 0x8888},                         # Call position independent code
                          override => $a->address + $inter->data);
   }

  ok Assemble eq=><<END, avx512=>1;
abcd
a: .... .... .... 6666
   rax: .... .... .... .111
   rax: .... .... .... 8888
END

  unlink $f;
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Tree::uniqueKeyString #TNasm::X86::Tree::getKeyString
  my $t = CreateArea->CreateTree(stringTree=>1);

  K(loop => 3)->for(sub
   {my $a = $t->uniqueKeyString(constantString("aaaa")); $a->outNL;
    my $b = $t->uniqueKeyString(constantString("bbbb")); $b->outNL;
   });

  for my $k(qw(aaaa bbbb ccccc))
   {PrintOutStringNL $k;
    $t->getKeyString(constantString $k); $t->found->outNL; $t->data->outNL;
   }

  ok Assemble eq => <<END, clocks=>4374;
count: .... .... .... ...0
count: .... .... .... ...1
count: .... .... .... ...0
count: .... .... .... ...1
count: .... .... .... ...0
count: .... .... .... ...1
aaaa
found  : .... .... .... ...1
data   : .... .... .... ...0
bbbb
found  : .... .... .... ...2
data   : .... .... .... ...1
ccccc
found  : .... .... .... ...0
data   : .... .... .... ...0
END
 }

test12: goto testX unless $test{12};

#latest:

#D1 Awaiting Classification                                                     # Routines that have not yet been classified.

sub BinarySearchD(%)                                                            #P Search for an ordered array of double words addressed by r15, of length held in r14 for a double word held in r13 and call the $then routine with the index in rax if found else call the $else routine.
 {my (%options) = @_;                                                           # Options
  my $Found     = delete $options{found}   // sub {PrintErrTraceBack "found"};  # Found an exact match
  my $Before    = delete $options{before}  // sub {PrintErrTraceBack "before"}; # The search key is before any key in the array
  my $After     = delete $options{after}   // sub {PrintErrTraceBack "after"};  # The search key is after any key in the array
  my $Between   = delete $options{between} // sub {PrintErrTraceBack "between"};# The search key is between two consecutive entries in the array being searched
  my $Empty     = delete $options{empty}   // sub {PrintErrTraceBack "empty"};  # The input array is empty
  my $Compare   = delete $options{compare} // sub {PrintErrTraceBack "compare"};# Compare routine
  my $Size      = delete $options{size}    // sub {PrintErrTraceBack "size"};   # Length of the array
  confess "Invalid options: ".join ", ", sort keys %options if keys %options;   # Complain about any invalid options

  PushR rax;                                                                    # Rax is used to communicate the results of the search back to the caller
  Block
   {my ($end, $start) = @_;                                                     # End, start label

    PushR my $low = r15, my $high = r14;                                        # Work registers modified by this routine

    Mov $low,  0;                                                               # Closed start of current range to search
    &$Size($high);                                                              # Open end of current range to search
    Cmp $high, 0;                                                               # Check we have a none empty array to search
    IfEq
    Then                                                                        # Empty array
     {PopRR $PushR[-$_]->@* for 1..1;
      &$Empty;
      Jmp $end;
     },
    Else                                                                        # Search non empty array
     {Dec $high;                                                                # Closed end of current range

      &$Compare($high);                                                         # Compare last element of array with search
      IfEq
      Then                                                                      # Found
       {Mov rax, $high;
        PopRR $PushR[-$_]->@* for 1..1;
        &$Found;
        Jmp $end;
       };
      IfGt
      Then                                                                      # After end of array
       {PopRR $PushR[-$_]->@* for 1..1;
        &$After;
        Jmp $end;
       };

      &$Compare($low);                                                          # Compare first element of array with search
      IfEq
      Then                                                                      # Found
       {Mov rax, $low;
        PopRR $PushR[-$_]->@* for 1..1;
        &$Found;
        Jmp $end;
       };
      IfLt
      Then                                                                      # Before start of array
       {PopRR $PushR[-$_]->@* for 1..1;
        &$Before;
        Jmp $end;
       };

      PushR my $loop = r13, my $mid = r12;

      uptoNTimes                                                                # Search a reasonable number of times now that we knoe that the key to be found is between the lower and upper limits of the array
       {Mov $mid, $low;                                                         # Find new mid point
        Add $mid, $high;                                                        # Sum of high and low
        Shr $mid, 1;                                                            # Average of high and low is the new mid point.
        &$Compare($mid);                                                        # Compare current element of array with search
        Pushfq;                                                                 # Save result of comparison
        IfEq
        Then                                                                    # Found
         {Mov rax, $mid;
          Popfq;
          PopRR $PushR[-$_]->@* for 1..2;
          &$Found;
          Jmp $end;
         };

        Mov rsi, $high;                                                         # Size of remaining range
        Sub rsi, $low;
        Cmp rsi, 1;

        IfLe
        Then                                                                    # Less than three elements in final range
         {&$Compare($high);                                                     # Compare high end of final range with search
          IfEq
          Then                                                                  # Found at high end of final range
           {Mov rax, $high;
            Popfq;
            PopRR $PushR[-$_]->@* for 1..2;
            &$Found;
            Jmp $end;
           };
          &$Compare($low);                                                      # Compare low end of final range with search
          IfEq
          Then                                                                  # Found at low end of final range
           {Mov rax, $low;
            Popfq;
            PopRR $PushR[-$_]->@* for 1..2;
            &$Found;
            Jmp $end;
           };
          Mov rax, $low;
          Popfq;
          PopRR $PushR[-$_]->@* for 1..2;
          &$Between;                                                            # Not found in final range so must be between the low element and the next element above it
          Jmp $end;
         };

        Popfq;                                                                  # Restore results of comparison
        IfGt                                                                    # Search argument is higher so move up
        Then
         {Mov $low, $mid;                                                       # New lower limit
         },
        Else                                                                    # Search argument is lower so move down
         {Mov $high, $mid;                                                      # New upper limit limit
         };
       } $loop, 999;                                                            # Enough to search all the particles in the universe if they could be ordered by some means
      pop @PushR  for 1..2;                                                     # This assembly code is unreachable so we only reduce the stack record in Perl rather than the stack in assembler
     };
   };
  PopR;
 }

#latest:
if (1) {                                                                        # Binary search on empty array
  Mov r14, 0;                                                                   # Size of array
  Mov r13, 1;                                                                   # Value to search for

  BinarySearchD
    empty => sub                                                                # Search empty array
     {PrintOutStringNL "Empty";                                                 # Not found
     },
    size  => sub {my ($r) = @_; Mov $r, 0};

  ok Assemble eq => <<END, avx512=>1;
Empty
END
 }

#latest:
if (1) {                                                                        # Binary search on populated array
  my $array = Rd(2, 4, 6, 8, 10, 12, 14, 16);

  for my $s(1..17)
#  for my $s(4)
   {Mov r13, $s;                                                                # Value to search for
    PrintOutString sprintf "%2d:", $s;

    BinarySearchD                                                               # Search
      size    => sub{my ($r) = @_; Mov $r, 8},
      found   => sub{PrintOutString " @ "; PrintOutRightInDecNL rax, 1},
      after   => sub{PrintOutStringNL " after"},
      before  => sub{PrintOutStringNL " before"},
      between => sub{PrintOutString " > "; PrintOutRightInDecNL rax, 1},
      compare => sub                                                            # Compare
       {my ($index) = @_;                                                       # Search key in register, current index in register
        Mov rdi, $array;
        Mov rsi, $s;
        Cmp esi, "[rdi+$index*4]";
       };
   }

  ok Assemble eq => <<END, avx512=>1;
 1: before
 2: @ 0
 3: > 0
 4: @ 1
 5: > 1
 6: @ 2
 7: > 2
 8: @ 3
 9: > 3
10: @ 4
11: > 4
12: @ 5
13: > 5
14: @ 6
15: > 6
16: @ 7
17: after
END
 }

#latest:;
if (1) {                                                                        # Binary search on populated array
  my $a = CreateArea;
  my $t = $a->CreateTree(stringTree=>1);
  $t->putKeyString(constantString("d"),        K(data => 1));
  $a->dump("AA", K(depth => 6));
  $t->dump("TT");

  $t->getKeyString(constantString("d"));  $t->found->outNL; $t->data->outNL;

  ok Assemble eq => <<END, avx512=>1;
AA
Area     Size:     4096    Used:      384
.... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | C0__ ____ .1__ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 64__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | 80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ ____ __.1 ____
.... .... .... .1.. | .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
TT
At:   C0                    length:    1,  data:  100,  nodes:  140,  first:   40, root, leaf
  Index:    0
  Keys :   80
  Data :    1
end
found  : .... .... .... ...1
data   : .... .... .... ...1
END
 }

#latest:;
if (1) {                                                                        # Binary search on populated array
  my $a = CreateArea;
  my $t = $a->CreateTree(stringTree=>1);
  $t->putKeyString(constantString("d2"), K(data => 2));
  $t->putKeyString(constantString("d1"), K(data => 1));
  $a->dump("AA", K(depth => 7));
  $t->dump("TT");

  $t->getKeyString(constantString("d1")); $t->found->outNL; $t->data->outNL;
  $t->getKeyString(constantString("d2")); $t->found->outNL; $t->data->outNL;

  ok Assemble eq => <<END, avx512=>1;
AA
Area     Size:     4096    Used:      448
.... .... .... ...0 | __10 ____ ____ ____  C0.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | C0__ ____ .1__ ____  .2__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 6432 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | 80.1 ____ 80__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .2__ ____ __.1 ____
.... .... .... .1.. | .1__ ____ .2__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .180 | 6431 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
TT
At:   C0                    length:    2,  data:  100,  nodes:  140,  first:   40, root, leaf
  Index:    0    1
  Keys :  180   80
  Data :    1    2
end
found  : .... .... .... ...1
data   : .... .... .... ...1
found  : .... .... .... ...2
data   : .... .... .... ...2
END
 }

#latest:;
if (1) {                                                                        # Binary search on populated array
  my $a = CreateArea;
  my $t = $a->CreateTree(stringTree=>1);
  $t->putKeyString(constantString("d"),   K data => 1);
  $t->putKeyString(constantString("dd"),  K data => 2);
  $a->dump("AA", K(depth => 7));
  $t->dump("TT");

  $t->getKeyString(constantString("d"));  $t->found->outNL; $t->data->outNL;
  $t->getKeyString(constantString("dd")); $t->found->outNL; $t->data->outNL;

  ok Assemble eq => <<END, avx512=>1;
AA
Area     Size:     4096    Used:      448
.... .... .... ...0 | __10 ____ ____ ____  C0.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | C0__ ____ .1__ ____  .2__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 64__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | 80__ ____ 80.1 ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .2__ ____ __.1 ____
.... .... .... .1.. | .1__ ____ .2__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .180 | 6464 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
TT
At:   C0                    length:    2,  data:  100,  nodes:  140,  first:   40, root, leaf
  Index:    0    1
  Keys :   80  180
  Data :    1    2
end
found  : .... .... .... ...1
data   : .... .... .... ...1
found  : .... .... .... ...2
data   : .... .... .... ...2
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Tree::putKeyString #TNasm::X86::Tree::getKeyString
  my $a = CreateArea;
  my $t = $a->CreateTree(stringTree=>1);
  $t->putKeyString(constantString("dddd444"),  K(data => 8));
  $t->putKeyString(constantString("dddd4444"), K(data => 9));
  $t->putKeyString(constantString("eeee5555"), K(data =>10));
  $t->putKeyString(constantString("bbbb2222"), K(data => 2));
  $t->putKeyString(constantString("cccc3333"), K(data => 7));
  $t->putKeyString(constantString("aaaa1111"), K(data => 1));
  $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443"),  K data => 6);
  $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442"),  K data => 5);
  $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441"),  K data => 4);
  $t->putKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440"),  K data => 3);
  $a->dump("AA", K(depth => 20));
  $t->dump("TT");

  $t->getKeyString(constantString("aaaa1111"));                                                          $t->data->outNL;
  $t->getKeyString(constantString("bbbb2222"));                                                          $t->data->outNL;
  $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44440")); $t->data->outNL;
  $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44441")); $t->data->outNL;
  $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44442")); $t->data->outNL;
  $t->getKeyString(constantString("cccc1111cccc2222cccc3333cccc4444cccc1111cccc2222cccc3333cccc44443")); $t->data->outNL;
  $t->getKeyString(constantString("cccc3333"));                                                          $t->data->outNL;
  $t->getKeyString(constantString("dddd444"));                                                           $t->data->outNL;
  $t->getKeyString(constantString("dddd4444"));                                                          $t->data->outNL;
  $t->getKeyString(constantString("eeee5555"));                                                          $t->data->outNL;

  ok Assemble eq => <<END, avx512=>1;
AA
Area     Size:     4096    Used:     1280
.... .... .... ...0 | __10 ____ ____ ____  __.5 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | C0__ ____ .1__ ____  .7__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | 6464 6464 3434 34__  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | 80.2 ____ __.2 ____  C0.2 ____ 40.2 ____  80__ ____ 80.1 ____  C0.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .7__ .4__ __.1 ____
.... .... .... .1.. | .1__ ____ .2__ ____  __.3 ____ .7__ ____  .8__ ____ .9__ ____  .A__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40.1 ____
.... .... .... .140 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ 40__ ____
.... .... .... .180 | 6464 6464 3434 3434  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .1C0 | 6565 6565 3535 3535  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2.. | 6262 6262 3232 3232  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .240 | 6363 6363 3333 3333  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .280 | 6161 6161 3131 3131  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .2C0 | 6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434  6363 6363 3131 3131  6363 6363 3232 3232  6363 6363 3333 3333  6363 6363 3434 3434
.... .... .... .3.. | 80.3 ____ .1__ ____  .4__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .340 | 33__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .380 | C0.4 ____ 80.4 ____  40.4 ____ 40.3 ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .4__ ____ C0.3 ____
.... .... .... .3C0 | .3__ ____ .4__ ____  .5__ ____ .6__ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.4 ____
.... .... .... .4.. | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ __.3 ____
.... .... .... .440 | 32__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .480 | 31__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... .4C0 | 30__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
TT
At:   C0                    length:    7,  data:  100,  nodes:  140,  first:   40, root, leaf,  trees:           100
  Index:    0    1    2    3    4    5    6
  Keys :  280  200  2C0  240   80  180  1C0
  Data :    1    2  38*    7    8    9   10
     At:  380               length:    4,  data:  3C0,  nodes:  400,  first:  300, root, leaf
       Index:    0    1    2    3
       Keys :  4C0  480  440  340
       Data :    3    4    5    6
     end
end
data   : .... .... .... ...1
data   : .... .... .... ...2
data   : .... .... .... ...3
data   : .... .... .... ...4
data   : .... .... .... ...5
data   : .... .... .... ...6
data   : .... .... .... ...7
data   : .... .... .... ...8
data   : .... .... .... ...9
data   : .... .... .... ...A
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::writeLibraryHeader  #TNasm::X86::Area::readLibraryHeader
  my $a = CreateArea;

  my $u = $a->CreateTree(stringTree=>1);
  $u->putKeyString(constantString("ab"), K key => 1);
  $u->putKeyString(constantString("ac"), K key => 3);
  $u->putKeyString(constantString("𝕒𝕒"), K key => 4);

  my %s = (ab => 2, 𝕒𝕒 =>8, 𝗮𝗮𝗮=>12, 𝝰𝝰𝝰𝝰=>16);                                 # Subroutine names and offsets

  $a->writeLibraryHeader({%s});
  my ($inter, $subroutines) = $a->readLibraryHeader($u);

  $inter->dump("TT");

  ok Assemble eq => <<END, clocks=>4374;
TT
At:  1C0                    length:    2,  data:  200,  nodes:  240,  first:   40, root, leaf
  Index:    0    1
  Keys :    1    4
  Data :    2    8
end
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::writeLibraryHeader  #TNasm::X86::Area::readLibraryHeader
  my $a = CreateArea;
  my $t = $a->CreateTree;

  K(loop => 16)->for(sub
   {my ($i, $start, $next, $end) = @_;
    my $T = $a->CreateTree;
    $t->put($i, $T);
    $t->size->outNL;
   });

  K(loop => 16)->for(sub
   {my ($i, $start, $next, $end) = @_;
    $t->popSubTree;
    $t->size->outNL;
   });

  ok Assemble eq => <<END, avx512=>1;
size of tree: .... .... .... ...1
size of tree: .... .... .... ...2
size of tree: .... .... .... ...3
size of tree: .... .... .... ...4
size of tree: .... .... .... ...5
size of tree: .... .... .... ...6
size of tree: .... .... .... ...7
size of tree: .... .... .... ...8
size of tree: .... .... .... ...9
size of tree: .... .... .... ...A
size of tree: .... .... .... ...B
size of tree: .... .... .... ...C
size of tree: .... .... .... ...D
size of tree: .... .... .... ...E
size of tree: .... .... .... ...F
size of tree: .... .... .... ..10
size of tree: .... .... .... ...F
size of tree: .... .... .... ...E
size of tree: .... .... .... ...D
size of tree: .... .... .... ...C
size of tree: .... .... .... ...B
size of tree: .... .... .... ...A
size of tree: .... .... .... ...9
size of tree: .... .... .... ...8
size of tree: .... .... .... ...7
size of tree: .... .... .... ...6
size of tree: .... .... .... ...5
size of tree: .... .... .... ...4
size of tree: .... .... .... ...3
size of tree: .... .... .... ...2
size of tree: .... .... .... ...1
size of tree: .... .... .... ...0
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Area::writeLibraryHeader  #TNasm::X86::Area::readLibraryHeader
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;
  $t->push($T);
  $a->dump("AA");
  $t->popSubTree;
  $a->dump("BB");

  ok Assemble eq => <<END, avx512=>1;
AA
Area     Size:     4096    Used:      384
.... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | C0__ ____ ____ ____  .1__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
BB
Area     Size:     4096    Used:      384
.... .... .... ...0 | __10 ____ ____ ____  80.1 ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  .1__ .1__ __.1 ____
END
 }

#latest:
if (1) {
  Mov rax, 0x2222;
  dRegIntoZmm(rax, 31, 8);
  dRegFromZmm(rdx, 31, 8);
  PrintOutRegisterInHex 31, rdx;

  ok Assemble eq => <<END, avx512=>1;
 zmm31: .... .... .... ...0  .... .... .... ...0 - .... .... .... ...0  .... .... .... ...0 + .... .... .... ...0  .... .... .... ...0 - .... .... .... 2222  .... .... .... ...0
   rdx: .... .... .... 2222
END
 }

#latest:;
if (1) {                                                                        #TNasm::X86::Tree::popSubTree
  my $a = CreateArea;
  my $t = $a->CreateTree;
  my $T = $a->CreateTree;
     $T->push(K key => 0x333);

  $t->push($T);
  $t->push(K key => 0x22);

  $t->pop;
  $t->found->outNL;
  $t->data->outNL;
  $t->subTree->outNL;

  my $P = $t->popSubTree;
  $t->found->outNL;
  $t->data->outNL;
  $t->subTree->outNL;
  PrintOutStringNL "PPPPP";
  $P->find(K key => 0);
  $P->found->outNL;
  $P->data->outNL;

  ok Assemble eq => <<END, avx512=>1;
found  : .... .... .... ...1
data   : .... .... .... ..22
subTree: .... .... .... ...0
found  : .... .... .... ...1
data   : .... .... .... ..80
subTree: .... .... .... ...1
PPPPP
found  : .... .... .... ...1
data   : .... .... .... .333
END
 }


# Test          Clocks           Bytes    Total Clocks     Total Bytes      Run Time     Assembler          Perl
#    1          12_425          25_440          12_425          25_440        0.2804          0.05          0.00  at /home/phil/perl/cpan/NasmX86/lib/Nasm/X86.pm line 19100
#    1          12_391          25_928          12_391          25_928        0.0787          0.02          0.00  at /home/phil/perl/cpan/NasmX86/lib/Nasm/X86.pm line 19103

#latest:
if (1) {                                                                        #TNasm::X86::Area::appendMemory
  my $a = CreateArea;
     $a->appendMemory(constantString("aaaa"));                                  # 82 48
     $a->dump("AA");

  ok Assemble eq => <<END, clocks=>12_391;
AA
Area     Size:     4096    Used:       68
.... .... .... ...0 | __10 ____ ____ ____  44__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 6161 6161 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Area::push #TNasm::X86::Area::peek #TNasm::X86::Area::pop #TNasm::X86::Area::pushZmm #TNasm::X86::Area::popZmm #TNasm::X86::Area::peekZmm #TNasm::X86::Area::stackVariableSize #TNasm::X86::Area::stackVariable #TNasm::X86::Area::stackSize
  my $a = CreateArea(stack=>1);

  $a->stackVariableSize->outNL;
  $a->dump("AA");
  $a->push(K key => 0x11111111);
  $a->stackVariableSize->outNL;
  $a->dump("AA");

  $a->push(K key => 0x22222222);                                                # 12
  $a->stackVariableSize->outNL;
  $a->dump("AA");

  PrintOutStringNL "Peek";
  $a->peek(1)->outNL;
  $a->stackVariableSize->outNL;
  $a->peek(2)->outNL;
  $a->stackVariableSize->outNL;
  $a->stackSize->outNL;

  PrintOutStringNL "stackVariable";
  $a->stackVariable(K key => 0)->outNL;
  $a->stackVariable(K key => 1)->outNL;

  PrintOutStringNL "Pop";
  my $s1 = $a->pop;                                                             #  9
     $s1->outNL;
  $a->stackVariableSize->outNL;
  my $s2 = $a->pop;
     $s2->outNL;
  $a->stackVariableSize->outNL;
  $a->dump("pop1");
  $a->dump("pop2");

  ClearRegisters zmm1, zmm2;

  K(key => 0x22222222)->qIntoZ(1, 16);
  K(key => 0x33333333)->qIntoZ(1, 32);
  K(key => 0x44444444)->qIntoZ(1, 48);

  K(key => 0x55555555)->qIntoZ(2, 16);
  K(key => 0x66666666)->qIntoZ(2, 32);
  K(key => 0x77777777)->qIntoZ(2, 48);

  PrintOutStringNL "Push";
  $a->pushZmm(1)->outNL;
  $a->pushZmm(2)->outNL;

  $a->dump("zmm 1, 2");
  $a->peekZmm(1, 1)->outNL;
  $a->peekZmm(2, 2)->outNL;
  PrintOutStringNL "Peek";
  PrintOutRegisterInHex zmm1, zmm2;

  $a->popZmm(3);
  $a->popZmm(4);
  PrintOutStringNL "Pop";
  PrintOutRegisterInHex zmm3, zmm4;

  ok Assemble eq => <<END, clocks=>391;
size: .... .... .... ...0
AA
Area     Size:     4096    Used:       64
.... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
size: .... .... .... ...1
AA
Area     Size:     4096    Used:       72
.... .... .... ...0 | __10 ____ ____ ____  48__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 1111 1111 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
size: .... .... .... ...2
AA
Area     Size:     4096    Used:       80
.... .... .... ...0 | __10 ____ ____ ____  50__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
Peek
pop: .... .... 2222 2222
size: .... .... .... ...2
pop: .... .... 1111 1111
size: .... .... .... ...2
stackSize: .... .... .... ..10
stackVariable
pop: .... .... 1111 1111
pop: .... .... 2222 2222
Pop
pop: .... .... 2222 2222
size: .... .... .... ...1
pop: .... .... 1111 1111
size: .... .... .... ...0
pop1
Area     Size:     4096    Used:       64
.... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
pop2
Area     Size:     4096    Used:       64
.... .... .... ...0 | __10 ____ ____ ____  40__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 1111 1111 ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
Push
zmmStackOffset: .... .... .... ..40
zmmStackOffset: .... .... .... ..80
zmm 1, 2
Area     Size:     4096    Used:      192
.... .... .... ...0 | __10 ____ ____ ____  C0__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | ____ ____ ____ ____  ____ ____ ____ ____  2222 2222 ____ ____  ____ ____ ____ ____  3333 3333 ____ ____  ____ ____ ____ ____  4444 4444 ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  5555 5555 ____ ____  ____ ____ ____ ____  6666 6666 ____ ____  ____ ____ ____ ____  7777 7777 ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
offset: .... .... .... ..80
offset: .... .... .... ..40
Peek
  zmm1: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
  zmm2: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
Pop
  zmm3: .... .... .... ...0  .... .... 7777 7777 - .... .... .... ...0  .... .... 6666 6666 + .... .... .... ...0  .... .... 5555 5555 - .... .... .... ...0  .... .... .... ...0
  zmm4: .... .... .... ...0  .... .... 4444 4444 - .... .... .... ...0  .... .... 3333 3333 + .... .... .... ...0  .... .... 2222 2222 - .... .... .... ...0  .... .... .... ...0
END
 }

#latest:;
if (1)
 {my $t = "𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔✕𝗔";
#    $t = $t x (1e4/length $t);

  my $p = &ParseUnisyn(constantString $t);
  ok Assemble eq => <<END, clocks=>42_291;
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Unisyn::Lex::composeUnisyn #TNasm::X86::Unisyn::Parse::dumpPostOrder
 {my $t = Nasm::X86::Unisyn::Lex::composeUnisyn "va w m+ w vb";
  is_deeply $t, "𝗮 ＋ 𝗯";
  my $p = &ParseUnisyn(constantString $t);
  $p->dumpPostOrder;
  ok Assemble eq => <<END;
._𝗮 
._𝗯
＋ 
END
 }

#latest:;
if (1)
 {my $a = CreateArea;
  $a->appendMemory(constantString((join '', map {chr $_} 0..255)x10));
  $a->write("z123.txt");                                                        # Save the area to the named file
  ok Assemble eq => <<END;
END
 }

#latest:;
if (1)
 {my $a = loadAreaIntoAssembly "z123.txt";
  $a->address->setReg(rax);
  Add rax, $a->dataOffset + ord('a');
  Mov al, "[rax]";
  And rax, 0xff;
  PrintOutRegisterInHex rax;
  ok Assemble eq => <<END;
   rax: .... .... .... ..61
END
 }

#latest:;
if (1)                                                                          # Place parser tables into an area #TNasm::X86::Area::appendVar #TNasm::X86::Unisyn::Lex::AlphabetsArray #TNasm::X86::Unisyn::Lex::PermissibleTransitionsArray
 {my $a = CreateArea;
  my ($alphabetN,    $alphabetA)    = Nasm::X86::Unisyn::Lex::AlphabetsArray;
  my ($transitionsN, $transitionsA) = Nasm::X86::Unisyn::Lex::PermissibleTransitionsArray;

  $a->appendVar(V address => "[$transitionsN]");
  $a->appendVar(V address => "[$alphabetN]");                                   # Save sizes at start if area where they can be easily found

  $a->appendMemory(V(address => $transitionsA), V size => "[$transitionsN]");   # Save transitions
  $a->appendMemory(V(address => $alphabetA),    V size => "[$alphabetN]");      # Save alphabets classification

  $a->write("z123.txt");                                                        # Save the area to the named file
  ok Assemble eq => <<END;
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Area::appendZmm #TloadZmm #TNasm::X86::Area::printOut #TNasm::X86::Area::write #TReadArea
 {LoadZmm 0, 61..61+63;

  my $a = CreateArea;
  $a->appendZmm(0);
  $a->printOut(0x44, 2);
  $a->dump("AA");
  $a->write(my $f = "aaa.txt");

  my $A = ReadArea $f;
  $A->dump("BB");
  ok Assemble eq => <<END;
AB
AA
Area     Size:     4096    Used:      128
.... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
BB
Area     Size:     4096    Used:      128
.... .... .... ...0 | __10 ____ ____ ____  80__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 3D3E 3F40 4142 4344  4546 4748 494A 4B4C  4D4E 4F50 5152 5354  5556 5758 595A 5B5C  5D5E 5F60 6162 6364  6566 6768 696A 6B6C  6D6E 6F70 7172 7374  7576 7778 797A 7B7C
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Area::char #TNasm::X86::Area::nl
 {my $a = CreateArea;
  $a->char('a');
  $a->nl;
  $a->dump("AA");
  ok Assemble eq => <<END;
AA
Area     Size:     4096    Used:       66
.... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Area::char #TNasm::X86::Area::nl
 {my $a = CreateArea;
  $a->char('a');
  $a->nl;
  $a->dump("AA");
  ok Assemble eq => <<END;
AA
Area     Size:     4096    Used:       66
.... .... .... ...0 | __10 ____ ____ ____  42__ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..40 | 61.A ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..80 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
.... .... .... ..C0 | ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____  ____ ____ ____ ____
END
 }

latest:;
if (1)                                                                          #TNasm::X86::Variable::at #TNasm::X86::Variable::addressExpr #TNasm::X86::Variable::call #TNasm::X86::Variable::clearBit #TNasm::X86::Variable::clearMaskBit #TNasm::X86::Variable::clearMemory #TNasm::X86::Variable::copyMemory #TNasm::X86::Variable::isRef #TNasm::X86::Variable::loadZmm #TNasm::X86::Variable::printOutMemory #TNasm::X86::Variable::printOutMemoryNL #TNasm::X86::Variable::setBit #TNasm::X86::Variable::setMaskBit #TNasm::X86::Variable::setMaskFirst #TNasm::X86::Variable::spaces #TNasm::X86::Variable::str
 {my $a = V(key => 0x123);
  is_deeply $a->at,           "[rbp-8*(2)]";
  is_deeply $a->addressExpr, "[rbp-8*(2)]";

  ok !$a->isRef;

  Mov rax, -1;
  Kmovq k1, rax;
  PrintOutRegisterInHex k1;

  K(key => 2)->clearBit(rax);
  PrintOutRegisterInHex rax;

  K(key => 2)->setBit(rax);
  PrintOutRegisterInHex rax;

  K(key => 3)->clearMaskBit(k1);
  PrintOutRegisterInHex k1;

  K(key => 3)->setMaskBit(k1);
  PrintOutRegisterInHex k1;

  K(key => 0)->clearMaskBit(k1);
  PrintOutRegisterInHex k1;

  ClearRegisters k1;
  K(key => 7)->setMaskFirst(k1);
  PrintOutRegisterInHex k1;

  K(key => 3)->spaces($stdout);
  PrintOutRegisterInHex k1;

  my $N = K size => 4096;
  my $A = $N->allocateMemory;
  $A->clearMemory($N);
  ClearMemory($A, $N);

  $A->setReg(rax);
  Mov "dword[rax]",   0x61626364;
  Mov "dword[rax+4]", 0x65666768;

  ($A+8)->copyMemory($A, K key => 8);

  $A->printOutMemory  (K(key => 16));
  $A->printOutMemoryNL(K(key => 16));

  K(K => Rd(0..63))->loadZmm(1);
  PrintOutRegisterInHex zmm1;

  ok Assemble eq => <<END;
    k1: .... .... .... ..-1
   rax: FFFF FFFF FFFF FFFB
   rax: .... .... .... ..-1
    k1: FFFF FFFF FFFF FFF7
    k1: .... .... .... ..-1
    k1: FFFF FFFF FFFF FFFE
    k1: .... .... .... ..7F
       k1: .... .... .... ..7F
dcbahgfedcbahgfedcbahgfedcbahgfe
  zmm1: .... ...F .... ...E  .... ...D .... ...C - .... ...B .... ...A  .... ...9 .... ...8 + .... ...7 .... ...6  .... ...5 .... ...4 - .... ...3 .... ...2  .... ...1 .... ....
END
 }

#latest:;
if (1)                                                                          #TNasm::X86::Unisyn::Lex::letterToNumber #TNasm::X86::Unisyn::Lex::numberToLetter
 {my ($n, $l) = Nasm::X86::Unisyn::Lex::letterToNumber;
  my ($N, $L) = Nasm::X86::Unisyn::Lex::numberToLetter;

  If $N > $n,
  Then
   {PrintErrTraceBack "Numbers to letters cannot be greater than letters to numbers";
   };

  $l->setReg(rax);
  $L->setReg(rbx);

  $n->for(sub
   {my ($i, $start, $next, $end) = @_;
    $i->setReg(rsi);                                                            # The character we want to test as utf32

    Mov edx, "[rax+4*rsi]";                                                     # The unisyn number of a letter
    Cmp edx, -1;
    Je $next;                                                                   # Not a unisyn chaarcter

    Mov r15d, "[rbx+4*rdx]";                                                    # r15 contains the utf32 representation of a unisyn letter

    Cmp r15, rsi;                                                               # Check that we retirved th chaarcter westarted with
    IfNe
    Then
     {$i->outNL("Mismatch: ");
      PrintOutRegisterInHex rsi, r15;
     }
   });

  ok Assemble eq => <<END;
END
 }

#latest:
if (1) {                                                                        #TNasm::X86::Tree::outAsUtf8 #TNasm::X86::Tree::append #TNasm::X86::Unisyn::Parse::traverseApplyingLibraryOperators #TParseUnisyn #TNasm::X86::Unisyn::Parse::dumpParseResult
  my $f = "zzzOperators.lib";                                                   # Methods to be called against each syntactic item

  my $library = Subroutine                                                      # This subroutine and all of the subroutines it contains will be saved in an area and that area will be written to a file from where it can be included via L<incBin> in subsequent assemblies.
   {my ($p, $s, $sub) = @_;

    Subroutine                                                                  # A contained routine that we wish to export to a file
     {my ($p, $s, $sub) = @_;
      PrintOutString "Ascii: ";

      my $parse  = $$s{parse};                                                  # Parse
      my $source = $parse->source;                                              # Source

      $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

      my $w = dSize;
      my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
      my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

     ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

     } name => "Ascii",
       structures => {parse => Nasm::X86::Unisyn::DescribeParse},
       parameters => [qw(offset)];

    Subroutine                                                                  # Another subroutine that will be exported because it is within the subroutine that is being exported as a library
     {my ($p, $s, $sub) = @_;
      PrintOutStringNL "Add";
     } name => "＋",
       structures => {parse => Nasm::X86::Unisyn::DescribeParse},
       parameters => [qw(offset)];

    Subroutine
     {my ($p, $s, $sub) = @_;
      PrintOutStringNL "Times";
     } name => "✕",
       structures => {parse => Nasm::X86::Unisyn::DescribeParse},
       parameters => [qw(offset)];

    Subroutine
     {my ($p, $s, $sub) = @_;
      PrintOutStringNL "And";
     } name => "𝕒𝕟𝕕",
       structures => {parse => Nasm::X86::Unisyn::DescribeParse},
       parameters => [qw(offset)];


    Subroutine
     {my ($p, $s, $sub) = @_;
      PrintOutString "Variable: ";

      my $parse  = $$s{parse};                                                  # Parse
      my $source = $parse->source;                                              # Source

      $parse->area->getZmmBlock($$p{offset}, 1);                                # Load current parse tree node

      my $w = dSize;
      my $length   = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::length);            # Length of ascii
      my $position = dFromZ(1, $w * Nasm::X86::Unisyn::Lex::position);          # Position in source

     ($source+$position)->printOutMemoryNL($length);                            # Print the ascii string

     } name => "Variable",
       structures => {parse => Nasm::X86::Unisyn::DescribeParse},
       parameters => [qw(offset)];

   } name => "operators",  parameters=>[qw(a b c)], export => $f;

  ok Assemble eq => <<END, avx512=>1;
END

  my $l = ReadArea $f;                                                          # Area containing subroutine library

  my ($A, $N) = constantString  qq(1＋𝗔✕𝗕＋𝗖𝕒𝕟𝕕2✕𝗔＋𝗕＋𝗖);                          # Utf8 string to parse

  my $p = ParseUnisyn($A, $N);                                                  # 10_445 Parse utf8 string  5_340 after single character lexical items, 4_950 after jump table

 $p->dumpParseResult;

 $p->traverseApplyingLibraryOperators($l);                                      # Traverse a parse tree applying a library of operators where they intersect with lexical items in the parse tree

# ✕ 2715
# ＋ ff0b

  ok Assemble eq => <<END, clocks=>20_921;
parseChar  : .... .... ...1 D5D6
parseFail  : .... .... .... ...0
position   : .... .... .... ..38
parseMatch : .... .... .... ...0
parseReason: .... .... .... ...0
𝕒𝕟𝕕
._＋
._._✕
._._._＋
._._._._1
._._._._𝗔
._._._𝗕
._._𝗖
._＋
._._＋
._._._✕
._._._._2
._._._._𝗔
._._._𝗕
._._𝗖
Ascii: 1
Variable: 𝗔
Add
Variable: 𝗕
Times
Variable: 𝗖
Add
Ascii: 2
Variable: 𝗔
Times
Variable: 𝗕
Add
Variable: 𝗖
Add
And
END
  unlink $f;
 };

#latest:
if (0) {                                                                        #TAssemble
  ok Assemble eq => <<END, mix=>1;
END
 }

testX:                                                                          # End of blocks of tests

done_testing;

#unlink $_ for qw(sde-footprint.txt sde-log.txt);

if (1)                                                                          # Summary of processing
 {my $s = $testsThatFailed == 0 ? 'SUCCESS' : sprintf("FAIL:%3d", $testsThatFailed);

   my $t = <<END,
#    Tests        Fails     Passes    Assemblies            Time           Bytes          Clocks
END
  my $r = sprintf(<<END,
# %8s        %5d     %6d    %10d        %8.2f    %12s    %12s
END
  $s,
  $testsThatFailed,
  $testsThatPassed,
  $assembliesPerformed,
  time - $start,
  numberWithUnderScores(totalBytesAssembled),
  numberWithUnderScores($instructionsExecuted));

  appendFile $resultFile, $t unless -e $resultFile;                             # Start the result file with the title line
  appendFile $resultFile, $r;                                                   # Result from this test

  say STDERR "\n$t$r";
  exit(1) if $testsThatFailed;                                                  # Show failure on gitHub
 }

# podDocumentation
# 91909 - Pilates of East Lake
