Name

Nasm::X86 - Generate X86 assembler code using Perl as a macro pre-processor.

Synopsis

Write and execute x64 instructions using perl as a macro assembler as shown in the following examples.

Avx512 instructions

Use avx512 instructions to do 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 00$P
END

Dynamic string held in an arena

Create a dynamic byte string, add some content to it, write the byte string to stdout:

my $a = CreateByteString;                                                     # Create a string
my $b = CreateByteString;                                                     # Create a string
$a->q('aa');
$b->q('bb');
$a->q('AA');
$b->q('BB');
$a->q('aa');
$b->q('bb');
$a->out;
$b->out;
PrintOutNL;
is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAAaabbBBbb
END

Process management

Start a child process and wait for it, printing out the process identifiers of each process involved:

Fork;                                                                         # Fork

Test rax,rax;
If                                                                            # Parent
 {Mov rbx, rax;
  WaitPid;
  PrintOutRegisterInHex rax;
  PrintOutRegisterInHex rbx;
  GetPid;                                                                     # Pid of parent as seen in parent
  Mov rcx,rax;
  PrintOutRegisterInHex rcx;
 }
sub                                                                           # Child
 {Mov r8,rax;
  PrintOutRegisterInHex r8;
  GetPid;                                                                     # Child pid as seen in child
  Mov r9,rax;
  PrintOutRegisterInHex r9;
  GetPPid;                                                                    # Parent pid as seen in child
  Mov r10,rax;
  PrintOutRegisterInHex r10;
 };

my $r = Assemble;

#    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

Read a file

Read this file:

Mov rax, Rs($0);                                                              # File to read
ReadFile;                                                                     # Read file
PrintOutMemory;                                                               # Print memory

my $r = Assemble;                                                             # Assemble and execute
ok index($r, readFile($0)) > -1;                                              # Output contains this file

Installation

The Intel Software Development Emulator will be required if you do not have a computer with the avx512 instruction set and wish to execute code containing these instructions. For details see:

https://software.intel.com/content/dam/develop/external/us/en/documents/downloads/sde-external-8.63.0-2021-01-18-lin.tar.bz2

The Networkwide Assembler is required to assemble the code produced For full details see:

https://github.com/philiprbrenan/NasmX86/blob/main/.github/workflows/main.yml

Execution Options

The Assemble(%options) function takes the following keywords to control assembly and execution of the assembled code:

To produce a named executable without running it, specify:

keep=>"executable file name"

To run the executable produced by Assemble(%options) without the Intel emulator, which is used by default if it is present, specify:

emulator=>0

Description

Generate X86 assembler code using Perl as a macro pre-processor.

Version "20210519".

The following sections describe the methods in each functional area of this module. For an alphabetic listing of all methods by name see Index.

Data

Layout data

SetLabel($l)

Set a label in the code section

   Parameter  Description
1  $l         Label

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;

  SetLabel $l;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

Ds(@d)

Layout bytes in memory and return their label

   Parameter  Description
1  @d         Data to be laid out

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;
PrintOutMemory;

ok Assemble =~ m(efghabcdmnopijkl)s;

Rs(@d)

Layout bytes in read only memory and return their label

   Parameter  Description
1  @d         Data to be laid out

Example:

Comment "Print a string from memory";
my $s = "Hello World";

Mov rax, Rs($s);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

Mov rdi, length $s;
PrintOutMemory;

ok Assemble =~ m(Hello World);


my $q = Rs('abababab');  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

Mov(rax, 1);
Mov(rbx, 2);
Mov(rcx, 3);
Mov(rdx, 4);
Mov(r8,  5);
Lea r9,  "[rax+rbx]";
PrintOutRegistersInHex;

my $r = Assemble;
ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;

Db(@bytes)

Layout bytes in the data segment and return their label

   Parameter  Description
1  @bytes     Bytes to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Dw(@words)

Layout words in the data segment and return their label

   Parameter  Description
1  @words     Words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Dd(@dwords)

Layout double words in the data segment and return their label

   Parameter  Description
1  @dwords    Double words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Dq(@qwords)

Layout quad words in the data segment and return their label

   Parameter  Description
1  @qwords    Quad words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Rb(@bytes)

Layout bytes in the data segment and return their label

   Parameter  Description
1  @bytes     Bytes to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Rw(@words)

Layout words in the data segment and return their label

   Parameter  Description
1  @words     Words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Rd(@dwords)

Layout double words in the data segment and return their label

   Parameter  Description
1  @dwords    Double words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Rq(@qwords)

Layout quad words in the data segment and return their label

   Parameter  Description
1  @qwords    Quad words to layout

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));
PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Float32($float)

32 bit float

   Parameter  Description
1  $float     Float

Float64($float)

64 bit float

   Parameter  Description
1  $float     Float

Registers

Operations on registers

Save and Restore

Saving and restoring registers via the stack

SaveFirstFour(@keep)

Save the first 4 parameter registers making any parameter registers read only

   Parameter  Description
1  @keep      Registers to mark as read only

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstFour()

Restore the first 4 parameter registers

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstFourExceptRax()

Restore the first 4 parameter registers except rax so it can return its value

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstFourExceptRaxAndRdi()

Restore the first 4 parameter registers except rax and rdi so we can return a pair of values

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  RestoreFirstFourExceptRaxAndRdi;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

SaveFirstSeven()

Save the first 7 parameter registers

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstSeven()

Restore the first 7 parameter registers

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstSevenExceptRax()

Restore the first 7 parameter registers except rax which is being used to return the result

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

RestoreFirstSevenExceptRaxAndRdi()

Restore the first 7 parameter registers except rax and rdi which are being used to return the results

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;

  RestoreFirstSevenExceptRaxAndRdi;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END

  ok 8 == RegisterSize rax;

ReorderSyscallRegisters(@registers)

Map the list of registers provided to the 64 bit system call sequence

   Parameter   Description
1  @registers  Registers

Example:

Mov rax, 1;  Mov rdi, 2;  Mov rsi,  3;  Mov rdx,  4;
Mov r8,  8;  Mov r9,  9;  Mov r10, 10;  Mov r11, 11;


ReorderSyscallRegisters   r8,r9;                                              # Reorder the registers for syscall  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;
PrintOutRegisterInHex rdi;

UnReorderSyscallRegisters r8,r9;                                              # Unreorder the registers to recover their original values
PrintOutRegisterInHex rax;
PrintOutRegisterInHex rdi;

ok Assemble =~ m(rax:.*08.*rdi:.*9.*rax:.*1.*rdi:.*2.*)s;

UnReorderSyscallRegisters(@registers)

Recover the initial values in registers that were reordered

   Parameter   Description
1  @registers  Registers

Example:

Mov rax, 1;  Mov rdi, 2;  Mov rsi,  3;  Mov rdx,  4;
Mov r8,  8;  Mov r9,  9;  Mov r10, 10;  Mov r11, 11;

ReorderSyscallRegisters   r8,r9;                                              # Reorder the registers for syscall
PrintOutRegisterInHex rax;
PrintOutRegisterInHex rdi;


UnReorderSyscallRegisters r8,r9;                                              # Unreorder the registers to recover their original values  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;
PrintOutRegisterInHex rdi;

ok Assemble =~ m(rax:.*08.*rdi:.*9.*rax:.*1.*rdi:.*2.*)s;

ReorderXmmRegisters(@registers) = map {"xmm$_"} @_;)

Map the list of xmm registers provided to 0-31

   Parameter                        Description
1  @registers) = map {"xmm$_"} @_;  Registers

Example:

  my $t = GenTree(2,2);                                                         # Tree description
  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2

    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root
    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion
   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3

    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root
    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion
   }

  cxr
   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();
    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};
   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

UnReorderXmmRegisters(@registers)

Recover the initial values in the xmm registers that were reordered

   Parameter   Description
1  @registers  Registers

Example:

  my $t = GenTree(2,2);                                                         # Tree description
  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2

    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root
    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion
   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3

    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root
    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion
   }

  cxr
   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();
    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};
   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

RegisterSize($r)

Return the size of a register

   Parameter  Description
1  $r         Register

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;
  RestoreFirstSevenExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;
  RestoreFirstFourExceptRaxAndRdi;
  PrintOutRegisterInHex rax, rdi;

  Bswap rax;
  PrintOutRegisterInHex rax;

  my $l = Label;
  Jmp $l;
  SetLabel $l;

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0002
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0001
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0002
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0001
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0000 0000 0000 0003
   rdi: 0000 0000 0000 0004
   rax: 0300 0000 0000 0000
END


  ok 8 == RegisterSize rax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

ClearRegisters(@registers)

Clear registers by setting them to zero

   Parameter   Description
1  @registers  Registers

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 =~ m(k0: 0000 0000 0000 0008.*k1: 0000 0000 0000 0000)s;

SetRegisterToMinusOne($register)

Set the specified register to -1

   Parameter  Description
1  $register  Register to set

Example:

SetRegisterToMinusOne rax;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;

ok Assemble =~ m(rax: FFFF FFFF FFFF FFFF);

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      Mask register to set
2  $start     Register containing start position or 0 for position 0
3  $length    Register containing end position

Example:

  Mov rax, 8;
  Mov rsi, -1;

  Inc rsi; SetMaskRegister(k0, rax, rsi); PrintOutRegisterInHex k0;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k1, rax, rsi); PrintOutRegisterInHex k1;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k2, rax, rsi); PrintOutRegisterInHex k2;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k3, rax, rsi); PrintOutRegisterInHex k3;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k4, rax, rsi); PrintOutRegisterInHex k4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k5, rax, rsi); PrintOutRegisterInHex k5;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k6, rax, rsi); PrintOutRegisterInHex k6;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Inc rsi; SetMaskRegister(k7, rax, rsi); PrintOutRegisterInHex k7;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply Assemble, <<END;
    k0: 0000 0000 0000 0000
    k1: 0000 0000 0000 0100
    k2: 0000 0000 0000 0300
    k3: 0000 0000 0000 0700
    k4: 0000 0000 0000 0F00
    k5: 0000 0000 0000 1F00
    k6: 0000 0000 0000 3F00
    k7: 0000 0000 0000 7F00
END

SetZF()

Set the zero flag

Example:

SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutZF;
ClearZF;
PrintOutZF;

SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutZF;

SetZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutZF;
ClearZF;
PrintOutZF;

ok Assemble =~ m(ZF=1.*ZF=0.*ZF=1.*ZF=1.*ZF=0)s;

ClearZF()

Clear the zero flag

Example:

SetZF;
PrintOutZF;

ClearZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutZF;
SetZF;
PrintOutZF;
SetZF;
PrintOutZF;

ClearZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutZF;

ok Assemble =~ m(ZF=1.*ZF=0.*ZF=1.*ZF=1.*ZF=0)s;

Tracking

Track the use of registers so that we do not accidently use unset registers or write into registers that are already in use.

Keep(@target)

Mark free registers so that they are not updated until we Free them or complain if the register is already in use.

   Parameter  Description
1  @target    Registers to keep

KeepSet($target)

Confirm that the specified registers are in use

   Parameter  Description
1  $target    Registers to keep

KeepPush(@target)

Push the current status of the specified registers and then mark them as free

   Parameter  Description
1  @target    Registers to keep

KeepPop(@target)

Reset the status of the specified registers to the status quo ante the last push

   Parameter  Description
1  @target    Registers to keep

KeepReturn(@target)

Pop the specified register and mark it as in use to effect a subroutine return with this register.

   Parameter  Description
1  @target    Registers to return

KeepFree(@target)

Free registers so that they can be reused

   Parameter  Description
1  @target    Registers to free

Arithmetic

Arithmetic operations on registers

Copy($target, $source)

Copy the source to the target register

   Parameter  Description
1  $target    Target register
2  $source    Source expression

Example:

  my $s = Rb(13, 1..13);
  my $t = Rb(1..64);
  my $source = rax;                                                             # Address to load from
  my $start  = rsi;                                                             # Start position in the zmm register
  my $length = rdi;                                                             # Length of copy


  Copy $source, $s;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  LoadShortStringFromMemoryToZmm 0, $s;                                         # Load a sample string
  KeepFree $source;
  PrintOutRegisterInHex xmm0;


  LoadZmmFromMemory 0, Increment(GetLengthOfShortString($start, 0)), Copy($length, 1), Copy($source, $t);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex xmm0;

  LoadZmmFromMemory 0, $start, $length, $source;
  PrintOutRegisterInHex xmm0;

  KeepFree $length;

  LoadZmmFromMemory 0, $start, Minus($length, Copy(r13, 56), $start), $source;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  SetLengthOfShortString 0, sil;                                                # Set current length of zmm0
  PrintOutRegisterInHex xmm0, zmm0;

  is_deeply Assemble, <<END;
  xmm0: 0000 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0001 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0201 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0201 0D0C 0B0A 0908   0706 0504 0302 0138
  zmm0: 0000 0000 0000 0000   2A29 2827 2625 2423   2221 201F 1E1D 1C1B   1A19 1817 1615 1413   1211 100F 0E0D 0C0B   0A09 0807 0605 0403   0201 0D0C 0B0A 0908   0706 0504 0302 0138
END

MaximumOfTwoRegisters($result, $first, $second)

Return in the specified register the value in the second register if it is greater than the value in the first register

   Parameter  Description
1  $result    Result register
2  $first     First register
3  $second    Second register

Example:

  Mov rax, 1;
  Mov rdi, 2;

  PrintOutRegisterInHex MaximumOfTwoRegisters r15, rax, rdi;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex MinimumOfTwoRegisters r14, rax, rdi;

  is_deeply Assemble, <<END;
   r15: 0000 0000 0000 0002
   r14: 0000 0000 0000 0001
END

MinimumOfTwoRegisters($result, $first, $second)

Return in the specified register the value in the second register if it is less than the value in the first register

   Parameter  Description
1  $result    Result register
2  $first     First register
3  $second    Second register

Example:

  Mov rax, 1;
  Mov rdi, 2;
  PrintOutRegisterInHex MaximumOfTwoRegisters r15, rax, rdi;

  PrintOutRegisterInHex MinimumOfTwoRegisters r14, rax, rdi;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply Assemble, <<END;
   r15: 0000 0000 0000 0002
   r14: 0000 0000 0000 0001
END

Increment($target, $amount)

Increment the specified register

   Parameter  Description
1  $target    Target register
2  $amount    Optional amount if not 1

Decrement($target, $amount)

Decrement the specified register

   Parameter  Description
1  $target    Target register
2  $amount    Optional amount if not 1

Plus($target, @source)

Add the last operands and place the result in the first operand

   Parameter  Description
1  $target    Target register
2  @source    Source registers

Example:

  Copy r15, 2;
  Copy r14, 3;
  KeepFree r15;

  Plus(r15, r15, r14);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex r15;
  Copy r13, 4;
  Minus(r12, r15, r13);
  PrintOutRegisterInHex r12;

  is_deeply Assemble, <<END;
   r15: 0000 0000 0000 0005
   r12: 0000 0000 0000 0001
END

Minus($target, $s1, $s2)

Subtract the third operand from the second operand and place the result in the first operand

   Parameter  Description
1  $target    Target register
2  $s1        Register to subtract from
3  $s2        Register to subtract

Example:

  Copy r15, 2;
  Copy r14, 3;
  KeepFree r15;
  Plus(r15, r15, r14);
  PrintOutRegisterInHex r15;
  Copy r13, 4;

  Minus(r12, r15, r13);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex r12;

  is_deeply Assemble, <<END;
   r15: 0000 0000 0000 0005
   r12: 0000 0000 0000 0001
END

Zmm

Operations on zmm registers

InsertIntoXyz($reg, $unit, $pos)

Shift and insert the specified word, double, quad from rax or the contents of xmm0 into the specified xyz register at the specified position shifting data above it to the left towards higher order bytes.

   Parameter  Description
1  $reg       Register to insert into
2  $unit      Width of insert
3  $pos       Position of insert in units from least significant byte starting at 0

Example:

my $s    = Rb 0..63;
Vmovdqu8 xmm0,"[$s]";                                                         # Number each byte
Vmovdqu8 ymm1,"[$s]";
Vmovdqu8 zmm2,"[$s]";
Vmovdqu8 zmm3,"[$s]";

SetRegisterToMinusOne rax;                                                    # Insert some ones

InsertIntoXyz xmm0, 2, 4;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


InsertIntoXyz ymm1, 4, 5;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


InsertIntoXyz zmm2, 8, 6;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


PrintOutRegisterInHex xmm0;                                                   # Print the insertions
PrintOutRegisterInHex ymm1;
PrintOutRegisterInHex zmm2;

ClearRegisters xmm0;                                                          # Insert some zeroes

InsertIntoXyz zmm3, 16, 2;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex zmm3;

my $r = Assemble;
ok $r =~ m(xmm0: 0D0C 0B0A 0908 FFFF   0706 0504 0302 0100);
ok $r =~ m(ymm1: 1B1A 1918 1716 1514   FFFF FFFF 1312 1110   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100);
ok $r =~ m(zmm2: 3736 3534 3332 3130   FFFF FFFF FFFF FFFF   2F2E 2D2C 2B2A 2928   2726 2524 2322 2120   1F1E 1D1C 1B1A 1918   1716 1514 1312 1110   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100);
ok $r =~ m(zmm3: 2F2E 2D2C 2B2A 2928   2726 2524 2322 2120   0000 0000 0000 0000   0000 0000 0000 0000   1F1E 1D1C 1B1A 1918   1716 1514 1312 1110   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100);

LoadTargetZmmFromSourceZmm($target, $targetOffset, $source, $sourceOffset, $length)

Load bytes into the numbered target zmm register at a register specified offset with source bytes from a numbered source zmm register at a specified register offset for a specified register length.

   Parameter      Description
1  $target        Number of zmm register to load
2  $targetOffset  Register containing start or 0 if from the start
3  $source        Numbered source zmm register
4  $sourceOffset  Register containing length
5  $length        Optional offset from stack top

Example:

  my $s = Rb(17, 1..17);
  LoadShortStringFromMemoryToZmm 0, $s;                                         # Load a sample string
  Keep zmm0;
  PrintOutRegisterInHex xmm0;

  LoadTargetZmmFromSourceZmm 1, Copy(rdi, 3), 0, Copy(rdx, 8), Copy(rsi, 2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex xmm1;
  KeepFree rdi;


  LoadTargetZmmFromSourceZmm 2, Copy(rdi, 4), 0, rdx, rsi;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex xmm2;

  Copy(zmm3, zmm0);
  PrintOutRegisterInHex xmm3;

  ClearRegisters zmm4;
  Lea rax, "[$s+4]";
  LoadZmmFromMemory 4, rdx, rsi, rax;
  Sub rax, 4;
  PrintOutRegisterInHex xmm4;

  is_deeply Assemble, <<END;
  xmm0: 0F0E 0D0C 0B0A 0908   0706 0504 0302 0111
  xmm1: 0000 0000 0000 0000   0000 0009 0800 0000
  xmm2: 0000 0000 0000 0000   0000 0908 0000 0000
  xmm3: 0F0E 0D0C 0B0A 0908   0706 0504 0302 0111
  xmm4: 0000 0000 0000 0504   0000 0000 0000 0000
END

  my $s = Rb(13, 1..13);
  my $t = Rb(1..64);
  my $source = rax;                                                             # Address to load from
  my $start  = rsi;                                                             # Start position in the zmm register
  my $length = rdi;                                                             # Length of copy

  Copy $source, $s;
  LoadShortStringFromMemoryToZmm 0, $s;                                         # Load a sample string
  KeepFree $source;
  PrintOutRegisterInHex xmm0;

  LoadZmmFromMemory 0, Increment(GetLengthOfShortString($start, 0)), Copy($length, 1), Copy($source, $t);
  PrintOutRegisterInHex xmm0;

  LoadZmmFromMemory 0, $start, $length, $source;
  PrintOutRegisterInHex xmm0;

  KeepFree $length;
  LoadZmmFromMemory 0, $start, Minus($length, Copy(r13, 56), $start), $source;
  SetLengthOfShortString 0, sil;                                                # Set current length of zmm0
  PrintOutRegisterInHex xmm0, zmm0;

  is_deeply Assemble, <<END;
  xmm0: 0000 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0001 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0201 0D0C 0B0A 0908   0706 0504 0302 010D
  xmm0: 0201 0D0C 0B0A 0908   0706 0504 0302 0138
  zmm0: 0000 0000 0000 0000   2A29 2827 2625 2423   2221 201F 1E1D 1C1B   1A19 1817 1615 1413   1211 100F 0E0D 0C0B   0A09 0807 0605 0403   0201 0D0C 0B0A 0908   0706 0504 0302 0138
END

LoadZmmFromMemory($target, $targetOffset, $length, $source)

Load bytes into the numbered target zmm register at a register specified offset with source bytes from memory addressed by a specified register for a specified register length from memory addressed by a specified register.

   Parameter      Description
1  $target        Number of zmm register to load
2  $targetOffset  Register containing start or 0 if from the start
3  $length        Register containing length
4  $source        Register addressing memory to load from

Structured Programming

Structured programming constructs

If($jump, $then, $else)

If

   Parameter  Description
1  $jump      Jump op code of variable
2  $then      Then - required
3  $else      Else - optional

Example:

Mov rax, 0;
Test rax,rax;
IfNz
 {PrintOutRegisterInHex rax;
 } sub
 {PrintOutRegisterInHex rbx;
 };
KeepFree rax;
Mov rax, 1;
Test rax,rax;
IfNz
 {PrintOutRegisterInHex rcx;
 } sub
 {PrintOutRegisterInHex rdx;
 };

ok Assemble =~ m(rbx.*rcx)s;

IfEq($then, $else)

If equal execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfNe($then, $else)

If not equal execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfNz($then, $else)

If not zero execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfLt($then, $else)

If less than execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfLe($then, $else)

If less than or equal execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfGt($then, $else)

If greater than execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

IfGe($then, $else)

If greater than or equal execute the then body else the else body

   Parameter  Description
1  $then      Then - required
2  $else      Else - optional

For($body, $register, $limit, $increment)

For - iterate the body as long as register is less than limit incrementing by increment each time

   Parameter   Description
1  $body       Body
2  $register   Register
3  $limit      Limit on loop
4  $increment  Increment on each iteration

Example:

For  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

 {PrintOutRegisterInHex rax
 } rax, 16, 1;

my $r = Assemble;
ok $r =~ m(( 0000){3} 0000)i;
ok $r =~ m(( 0000){3} 000F)i;

ForIn($full, $last, $register, $limit, $increment)

For - iterate the body as long as register plus increment is less than than limit incrementing by increment each time

   Parameter   Description
1  $full       Body for full block
2  $last       Body for last block
3  $register   Register
4  $limit      Limit on loop
5  $increment  Increment on each iteration

ForEver($body)

Iterate for ever

   Parameter  Description
1  $body      Body to iterate

Macro($body, %options)

Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub

   Parameter  Description
1  $body      Body
2  %options   Options.

Subroutine($body, %options)

Create a subroutine that can be called in assembler code

   Parameter  Description
1  $body      Body
2  %options   Options.

Nasm::X86::Sub::call($sub, @parameters)

Call a sub passing it some parameters

   Parameter    Description
1  $sub         Subroutine descriptor
2  @parameters  Parameter variables

cr($body, @registers)

Call a subroutine with a reordering of the registers.

   Parameter   Description
1  $body       Code to execute with reordered registers
2  @registers  Registers to reorder

cxr($body, @registers)

Call a subroutine with a reordering of the xmm registers.

   Parameter   Description
1  $body       Code to execute with reordered registers
2  @registers  Registers to reorder

Example:

  my $t = GenTree(2,2);                                                         # Tree description
  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2


    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3


    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   }


  cxr  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();
    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};
   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

Comment(@comment)

Insert a comment into the assembly code

   Parameter  Description
1  @comment   Text of comment

Example:

Comment "Print a string from memory";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

my $s = "Hello World";
Mov rax, Rs($s);
Mov rdi, length $s;
PrintOutMemory;

ok Assemble =~ m(Hello World);

DComment(@comment)

Insert a comment into the data segment

   Parameter  Description
1  @comment   Text of comment

RComment(@comment)

Insert a comment into the read only data segment

   Parameter  Description
1  @comment   Text of comment

Print

Print

PrintErrNL()

Print a new line to stderr

PrintErrString($string)

Print a constant string to stderr.

   Parameter  Description
1  $string    String

PrintErrStringNL($string)

Print a new line to stderr

   Parameter  Description
1  $string    String

Example:

  PrintOutStringNL "Hello World";

  PrintErrStringNL "Hello World";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply Assemble, <<END;
Hello World
Hello World
END

PrintOutNL()

Print a new line to stdout

Example:

my $q = Rs('abababab');
Mov(rax, "[$q]");
PrintOutString "rax: ";
PrintOutRaxInHex;

PrintOutNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

Xor rax, rax;
PrintOutString "rax: ";
PrintOutRaxInHex;

PrintOutNL;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


ok Assemble =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;

PrintOutString($string)

Print a constant string to sysout.

   Parameter  Description
1  $string    String

PrintOutStringNL($string)

Print a constant string to sysout followed by new line

   Parameter  Description
1  $string    String

Example:

  PrintOutStringNL "Hello World";  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintErrStringNL "Hello World";

  is_deeply Assemble, <<END;
Hello World
Hello World
END

  my $t = GenTree(2,2);                                                         # Tree description
  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2

    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root
    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion
   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3

    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root
    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion
   }

  cxr
   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();

    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

PrintOutRaxInHex()

Write the content of register rax to stderr in hexadecimal in big endian notation

Example:

my $q = Rs('abababab');
Mov(rax, "[$q]");
PrintOutString "rax: ";

PrintOutRaxInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutNL;
Xor rax, rax;
PrintOutString "rax: ";

PrintOutRaxInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutNL;

ok Assemble =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;

PrintOutRaxInReverseInHex()

Write the content of register rax to stderr in hexadecimal in little endian notation

Example:

  Mov rax, 0x07654321;
  Shl rax, 32;
  Or  rax, 0x07654321;
  PushR rax;

  PrintOutRaxInHex;
  PrintOutNL;

  PrintOutRaxInReverseInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutNL;
  KeepFree rax;

  Mov rax, rsp;
  Mov rdi, 8;
  PrintOutMemoryInHex;
  PrintOutNL;
  PopR rax;
  KeepFree rax, rdi;

  Mov rax, 4096;
  PushR rax;
  Mov rax, rsp;
  Mov rdi, 8;
  PrintOutMemoryInHex;
  PrintOutNL;
  PopR rax;

  is_deeply Assemble, <<END;
0765 4321 0765 4321
2143 6507 2143 6507
2143 6507 2143 6507
0010 0000 0000 0000
END

PrintOutRegisterInHex(@r)

Print any register as a hex string

   Parameter  Description
1  @r         Name of the register to print

Example:

my $q = Rs(('a'..'p')x4);
Mov r8,"[$q]";

PrintOutRegisterInHex r8;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


ok Assemble =~ m(r8: 6867 6665 6463 6261)s;

PrintOutRegistersInHex()

Print the general purpose registers in hex

Example:

my $q = Rs('abababab');
Mov(rax, 1);
Mov(rbx, 2);
Mov(rcx, 3);
Mov(rdx, 4);
Mov(r8,  5);
Lea r9,  "[rax+rbx]";

PrintOutRegistersInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


my $r = Assemble;
ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;

PrintOutZF()

Print the zero flag without disturbing it

Example:

SetZF;

PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

ClearZF;

PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

SetZF;

PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

SetZF;

PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

ClearZF;

PrintOutZF;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


ok Assemble =~ m(ZF=1.*ZF=0.*ZF=1.*ZF=1.*ZF=0)s;

Variables

Variable definitions and operations

Scopes

Each variable is contained in a scope in an effort to detect references to out of scope variables

Scope($name)

Create and stack a new scope and continue with it as the current scope

   Parameter  Description
1  $name      Scope name

Example:

if (1)

 {my $start = Scope(start);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $s1    = Scope(s1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $s2    = Scope(s2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  is_deeply $s2->depth, 2;
  is_deeply $s2->name,  q(s2);
  ScopeEnd;


  my $t1    = Scope(t1);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $t2    = Scope(t2);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  is_deeply $t1->depth, 2;
  is_deeply $t1->name,  q(t1);
  is_deeply $t2->depth, 3;
  is_deeply $t2->name,  q(t2);

  ok  $s1->currentlyVisible;
  ok !$s2->currentlyVisible;

  ok  $s1->contains($t2);
  ok !$s2->contains($t2);

  ScopeEnd;

  is_deeply $s1->depth, 1;
  is_deeply $s1->name,  q(s1);
  ScopeEnd;
 }

ScopeEnd()

End the current scope and continue with the containing parent scope

Example:

if (1)
 {my $start = Scope(start);
  my $s1    = Scope(s1);
  my $s2    = Scope(s2);
  is_deeply $s2->depth, 2;
  is_deeply $s2->name,  q(s2);

  ScopeEnd;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $t1    = Scope(t1);
  my $t2    = Scope(t2);
  is_deeply $t1->depth, 2;
  is_deeply $t1->name,  q(t1);
  is_deeply $t2->depth, 3;
  is_deeply $t2->name,  q(t2);

  ok  $s1->currentlyVisible;
  ok !$s2->currentlyVisible;

  ok  $s1->contains($t2);
  ok !$s2->contains($t2);


  ScopeEnd;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply $s1->depth, 1;
  is_deeply $s1->name,  q(s1);

  ScopeEnd;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

 }

Scope::contains($parent, $child)

Check that the named parent scope contains the specified child scope. If no child scope is supplied we use the current scope to check that the parent scope is currently visible

   Parameter  Description
1  $parent    Parent scope
2  $child     Child scope

Example:

if (1)
 {my $start = Scope(start);
  my $s1    = Scope(s1);
  my $s2    = Scope(s2);
  is_deeply $s2->depth, 2;
  is_deeply $s2->name,  q(s2);
  ScopeEnd;

  my $t1    = Scope(t1);
  my $t2    = Scope(t2);
  is_deeply $t1->depth, 2;
  is_deeply $t1->name,  q(t1);
  is_deeply $t2->depth, 3;
  is_deeply $t2->name,  q(t2);

  ok  $s1->currentlyVisible;
  ok !$s2->currentlyVisible;

  ok  $s1->contains($t2);
  ok !$s2->contains($t2);

  ScopeEnd;

  is_deeply $s1->depth, 1;
  is_deeply $s1->name,  q(s1);
  ScopeEnd;
 }

Scope::currentlyVisible($scope)

Check that the named parent scope is currently visible

   Parameter  Description
1  $scope     Scope to check for visibility

Example:

if (1)
 {my $start = Scope(start);
  my $s1    = Scope(s1);
  my $s2    = Scope(s2);
  is_deeply $s2->depth, 2;
  is_deeply $s2->name,  q(s2);
  ScopeEnd;

  my $t1    = Scope(t1);
  my $t2    = Scope(t2);
  is_deeply $t1->depth, 2;
  is_deeply $t1->name,  q(t1);
  is_deeply $t2->depth, 3;
  is_deeply $t2->name,  q(t2);

  ok  $s1->currentlyVisible;
  ok !$s2->currentlyVisible;

  ok  $s1->contains($t2);
  ok !$s2->contains($t2);

  ScopeEnd;

  is_deeply $s1->depth, 1;
  is_deeply $s1->name,  q(s1);
  ScopeEnd;
 }

Definitions

Variable definitions

Variable($size, $name, $expr)

Create a new variable with the specified size and name initialized via an expression

   Parameter  Description
1  $size      Size as a power of 2
2  $name      Name of variable
3  $expr      Optional expression initializing variable

Vb($name, $expr)

Define a byte variable

   Parameter  Description
1  $name      Name of variable
2  $expr      Initializing expression

Vw($name, $expr)

Define a word variable

   Parameter  Description
1  $name      Name of variable
2  $expr      Initializing expression

Vd($name, $expr)

Define a double word variable

   Parameter  Description
1  $name      Name of variable
2  $expr      Initializing expression

Vq($name, $expr)

Define a quad variable

   Parameter  Description
1  $name      Name of variable
2  $expr      Initializing expression

Example:

  my $a = Vq(a, 3);   $a->print;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  my $c = $a +  2;    $c->print;
  my $d = $c -  $a;   $d->print;
  my $e = $d == 2;    $e->print;
  my $f = $d != 2;    $f->print;
  my $g = $a *  2;    $g->print;
  my $h = $g /  2;    $h->print;
  my $i = $a %  2;    $i->print;

  If ($a == 3, sub{PrintOutStringNL "a == 3"});

  is_deeply Assemble, <<END;
0300 0000 0000 0000
0500 0000 0000 0000
0200 0000 0000 0000
0100 0000 0000 0000
0000 0000 0000 0000
0600 0000 0000 0000
0300 0000 0000 0000
0100 0000 0000 0000
a == 3
END

VxyzInit($var, @expr)

Initialize an xyz register from general purpose registers

   Parameter  Description
1  $var       Variable
2  @expr      Initializing general purpose registers or undef

Vx($name, @expr)

Define an xmm variable

   Parameter  Description
1  $name      Name of variable
2  @expr      Initializing expression

Vy($name, @expr)

Define an ymm variable

   Parameter  Description
1  $name      Name of variable
2  @expr      Initializing expression

Vz($name, @expr)

Define an zmm variable

   Parameter  Description
1  $name      Name of variable
2  @expr      Initializing expression

Vr($name, $ref)

Define a reference variable

   Parameter  Description
1  $name      Name of variable
2  $ref       Variable being referenced

Operations

Variable operations

Variable::address($left, $offset)

Get the address of a variable with an optional offset

   Parameter  Description
1  $left      Left variable
2  $offset    Optional offset

Variable::copy($left, $right)

Copy one variable into another

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Variable::copyRef($left, $right)

Copy one variable into an referenced variable

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Variable::copyAddress($left, $right)

Copy a reference to a variable

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Variable::equals($op, $left, $right)

Equals operator

   Parameter  Description
1  $op        Operator
2  $left      Left variable
3  $right     Right variable

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

Variable::plusAssign($left, $right)

Implement plus and assign

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Variable::minusAssign($left, $right)

Implement minus and assign

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

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

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

Example:

  my $a = Vq(a, 3);   $a->print;
  my $c = $a +  2;    $c->print;
  my $d = $c -  $a;   $d->print;
  my $e = $d == 2;    $e->print;
  my $f = $d != 2;    $f->print;
  my $g = $a *  2;    $g->print;
  my $h = $g /  2;    $h->print;
  my $i = $a %  2;    $i->print;

  If ($a == 3, sub{PrintOutStringNL "a == 3"});

  is_deeply Assemble, <<END;
0300 0000 0000 0000
0500 0000 0000 0000
0200 0000 0000 0000
0100 0000 0000 0000
0000 0000 0000 0000
0600 0000 0000 0000
0300 0000 0000 0000
0100 0000 0000 0000
a == 3
END

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

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

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

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

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

Variable::boolean($sub, $op, $left, $right)

Combine the left hand variable with the right hand variable via a boolean operator

   Parameter  Description
1  $sub       Operator
2  $op        Operator name
3  $left      Left variable
4  $right     Right variable

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

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

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

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

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

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

Variable::print($left)

Write the value of a variable on stdout

   Parameter  Description
1  $left      Left variable

Example:

  my $a = Vq(a, 3);   $a->print;
  my $c = $a +  2;    $c->print;
  my $d = $c -  $a;   $d->print;
  my $e = $d == 2;    $e->print;
  my $f = $d != 2;    $f->print;
  my $g = $a *  2;    $g->print;
  my $h = $g /  2;    $h->print;
  my $i = $a %  2;    $i->print;

  If ($a == 3, sub{PrintOutStringNL "a == 3"});

  is_deeply Assemble, <<END;
0300 0000 0000 0000
0500 0000 0000 0000
0200 0000 0000 0000
0100 0000 0000 0000
0000 0000 0000 0000
0600 0000 0000 0000
0300 0000 0000 0000
0100 0000 0000 0000
a == 3
END

  my $a = Vq(a, 3); $a->dump;
  my $b = Vq(b, 2); $b->dump;
  my $c = $a +  $b; $c->dump;
  my $d = $c -  $a; $d->dump;
  my $e = $d == $b; $e->dump;
  my $f = $d != $b; $f->dump;
  my $g = $a *  $b; $g->dump;
  my $h = $g /  $b; $h->dump;
  my $i = $a %  $b; $i->dump;
  is_deeply Assemble, <<END;
a: 0000 0000 0000 0003
b: 0000 0000 0000 0002
(a add b): 0000 0000 0000 0005
((a add b) sub a): 0000 0000 0000 0002
(((a add b) sub a) eq b): 0000 0000 0000 0001
(((a add b) sub a) ne b): 0000 0000 0000 0000
(a times b): 0000 0000 0000 0006
((a times b) / b): 0000 0000 0000 0003
(a % b): 0000 0000 0000 0001
END

Variable::dump($left, $title)

Dump the value of a variable on stdout

   Parameter  Description
1  $left      Left variable
2  $title     Optional title

Example:

  my $a = Vq(a, 3); $a->dump;
  my $b = Vq(b, 2); $b->dump;
  my $c = $a +  $b; $c->dump;
  my $d = $c -  $a; $d->dump;
  my $e = $d == $b; $e->dump;
  my $f = $d != $b; $f->dump;
  my $g = $a *  $b; $g->dump;
  my $h = $g /  $b; $h->dump;
  my $i = $a %  $b; $i->dump;
  is_deeply Assemble, <<END;
a: 0000 0000 0000 0003
b: 0000 0000 0000 0002
(a add b): 0000 0000 0000 0005
((a add b) sub a): 0000 0000 0000 0002
(((a add b) sub a) eq b): 0000 0000 0000 0001
(((a add b) sub a) ne b): 0000 0000 0000 0000
(a times b): 0000 0000 0000 0006
((a times b) / b): 0000 0000 0000 0003
(a % b): 0000 0000 0000 0001
END

Variable::debug($left)

Dump the value of a variable on stdout with an indication of where the dump came from

   Parameter  Description
1  $left      Left variable

Variable::isRef($variable)

Check whether the specified variable is a reference to another variable

   Parameter  Description
1  $variable  Variable

Variable::setReg($variable, $register, @registers)

Set the named registers from the content of the variable

   Parameter   Description
1  $variable   Variable
2  $register   Register to load
3  @registers  Optional further registers to load

Variable::getReg($variable, $register, @registers)

Load the variable from the named registers

   Parameter   Description
1  $variable   Variable
2  $register   Register to load
3  @registers  Optional further registers to load from

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

Variable::inc($left)

Increment a variable

   Parameter  Description
1  $left      Variable

Variable::dec($left)

Decrement a variable

   Parameter  Description
1  $left      Variable

Variable::str($left)

The name of the variable

   Parameter  Description
1  $left      Variable

Variable::min($left, $right)

Minimum of two variables

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Example:

  my $a = Vq("a", 1);
  my $b = Vq("b", 2);
  my $c = $a->min($b);
  my $d = $a->max($b);
  $a->dump;
  $b->dump;
  $c->dump;
  $d->dump;

  is_deeply Assemble,<<END;
a: 0000 0000 0000 0001
b: 0000 0000 0000 0002
Minimum(a, b): 0000 0000 0000 0001
Maximum(a, b): 0000 0000 0000 0002
END

Variable::max($left, $right)

Maximum of two variables

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Example:

  my $a = Vq("a", 1);
  my $b = Vq("b", 2);
  my $c = $a->min($b);
  my $d = $a->max($b);
  $a->dump;
  $b->dump;
  $c->dump;
  $d->dump;

  is_deeply Assemble,<<END;
a: 0000 0000 0000 0001
b: 0000 0000 0000 0002
Minimum(a, b): 0000 0000 0000 0001
Maximum(a, b): 0000 0000 0000 0002
END

Variable::and($left, $right)

And two variables

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

Variable::or($left, $right)

Or two variables

   Parameter  Description
1  $left      Left variable
2  $right     Right variable

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

Example:

  my $start  = Vq("Start",  7);
  my $length = Vq("Length", 3);
  $start->setMask($length, k7);
  PrintOutRegisterInHex k7;

  is_deeply Assemble, <<END;
    k7: 0000 0000 0000 0380
END

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

Example:

  my $s = Rb(0..128);
  my $source = Vq(Source, $s);

  if (1)                                                                        # First block
   {my $offset = Vq(Offset, 7);
    my $length = Vq(Length, 3);
    $source->setZmm(0, $offset, $length);
   }

  if (1)                                                                        # Second block
   {my $offset = Vq(Offset, 33);
    my $length = Vq(Length, 12);
    $source->setZmm(0, $offset, $length);
   }

  PrintOutRegisterInHex zmm0;

  is_deeply Assemble, <<END;
  zmm0: 0000 0000 0000 0000   0000 0000 0000 0000   0000 000B 0A09 0807   0605 0403 0201 0000   0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0201   0000 0000 0000 0000
END

  my $a = Vz a, Rb((map {"0x${_}0"} 0..9, 'a'..'f')x4);
  my $b = Vz b, Rb((map {"0x0${_}"} 0..9, 'a'..'f')x4);

   $a      ->loadZmm(0);                                                        # Show variable in zmm0
   $b      ->loadZmm(1);                                                        # Show variable in zmm1

  ($a + $b)->loadZmm(2);                                                        # Add bytes      and show in zmm2
  ($a - $b)->loadZmm(3);                                                        # Subtract bytes and show in zmm3

  PrintOutRegisterInHex "zmm$_" for 0..3;

  is_deeply Assemble, <<END;
  zmm0: F0E0 D0C0 B0A0 9080   7060 5040 3020 1000   F0E0 D0C0 B0A0 9080   7060 5040 3020 1000   F0E0 D0C0 B0A0 9080   7060 5040 3020 1000   F0E0 D0C0 B0A0 9080   7060 5040 3020 1000
  zmm1: 0F0E 0D0C 0B0A 0908   0706 0504 0302 0100   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100   0F0E 0D0C 0B0A 0908   0706 0504 0302 0100
  zmm2: FFEE DDCC BBAA 9988   7766 5544 3322 1100   FFEE DDCC BBAA 9988   7766 5544 3322 1100   FFEE DDCC BBAA 9988   7766 5544 3322 1100   FFEE DDCC BBAA 9988   7766 5544 3322 1100
  zmm3: E1D2 C3B4 A596 8778   695A 4B3C 2D1E 0F00   E1D2 C3B4 A596 8778   695A 4B3C 2D1E 0F00   E1D2 C3B4 A596 8778   695A 4B3C 2D1E 0F00   E1D2 C3B4 A596 8778   695A 4B3C 2D1E 0F00
END

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

Variable::saveZmm($target, $zmm)

Save bytes into the memory addressed by the target variable from the numbered zmm register.

   Parameter  Description
1  $target    Variable containing the address of the source
2  $zmm       Number of zmm to put

getBwdqFromZmmAsVariable($size, $zmm, $offset)

Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable

   Parameter  Description
1  $size      Size of get
2  $zmm       Numbered zmm
3  $offset    Offset in bytes

getBFromZmmAsVariable($zmm, $offset)

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

getWFromZmmAsVariable($zmm, $offset)

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

getDFromZmmAsVariable($zmm, $offset)

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

Example:

  my $s = Rb(0..8);
  my $c = Vq("Content",   "[$s]");
     $c->putBIntoZmm(0,  4);
     $c->putWIntoZmm(0,  6);
     $c->putDIntoZmm(0, 10);
     $c->putQIntoZmm(0, 16);
  PrintOutRegisterInHex zmm0;
  getBFromZmmAsVariable(0, 12)->dump;
  getWFromZmmAsVariable(0, 12)->dump;

  getDFromZmmAsVariable(0, 12)->dump;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  getQFromZmmAsVariable(0, 12)->dump;

  is_deeply Assemble, <<END;
  zmm0: 0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0706 0504 0302 0100   0000 0302 0100 0000   0100 0000 0000 0000
b at offset 12 in zmm0: 0000 0000 0000 0002
w at offset 12 in zmm0: 0000 0000 0000 0302
d at offset 12 in zmm0: 0000 0000 0000 0302
q at offset 12 in zmm0: 0302 0100 0000 0302
END

getQFromZmmAsVariable($zmm, $offset)

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

Variable::putBwdqIntoZmm($content, $size, $zmm, $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  $zmm       Numbered zmm
4  $offset    Offset in bytes

Variable::putBIntoZmm($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

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

Variable::putDIntoZmm($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

Example:

  my $s = Rb(0..8);
  my $c = Vq("Content",   "[$s]");
     $c->putBIntoZmm(0,  4);
     $c->putWIntoZmm(0,  6);
     $c->putDIntoZmm(0, 10);
     $c->putQIntoZmm(0, 16);
  PrintOutRegisterInHex zmm0;
  getBFromZmmAsVariable(0, 12)->dump;
  getWFromZmmAsVariable(0, 12)->dump;
  getDFromZmmAsVariable(0, 12)->dump;
  getQFromZmmAsVariable(0, 12)->dump;

  is_deeply Assemble, <<END;
  zmm0: 0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0000 0000 0000 0000   0706 0504 0302 0100   0000 0302 0100 0000   0100 0000 0000 0000
b at offset 12 in zmm0: 0000 0000 0000 0002
w at offset 12 in zmm0: 0000 0000 0000 0302
d at offset 12 in zmm0: 0000 0000 0000 0302
q at offset 12 in zmm0: 0302 0100 0000 0302
END

Variable::putQIntoZmm($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

Variable::clearMemory($memory)

Clear the memory described in this variable

   Parameter  Description
1  $memory    Variable describing memory as returned by Allocate Memory

Variable::copyMemoryFrom($target, $source)

Copy from one block of memory to another

   Parameter  Description
1  $target    Variable describing the target
2  $source    Variable describing the source

Variable::printOutMemoryInHex($memory)

Print allocated memory in hex

   Parameter  Description
1  $memory    Variable describing the memory

Variable::freeMemory($memory)

Free the memory described in this variable

   Parameter  Description
1  $memory    Variable describing memory as returned by Allocate Memory

Example:

my $N = Vq(size, 2048);
my $q = Rs('a'..'p');
AllocateMemory->call($N, my $address = Vq(address));
$address->dump;

Vmovdqu8 xmm0, "[$q]";
$address->setReg(rax);
Vmovdqu8 "[rax]", xmm0;
Mov rdi, 16;
PrintOutMemory;
PrintOutNL;

FreeMemory->call(address => $address, size=> $N);

ok Assemble =~ m(address: 0000.*abcdefghijklmnop)s;

Variable::for($limit, $body)

Iterate the body from 0 limit times.

   Parameter  Description
1  $limit     Limit
2  $body      Body

Example:

  Vq(limit,10)->for(sub
   {my ($i, $start, $next, $end) = @_;
    $i->print;
   });

  is_deeply Assemble, <<END;
0000 0000 0000 0000
0100 0000 0000 0000
0200 0000 0000 0000
0300 0000 0000 0000
0400 0000 0000 0000
0500 0000 0000 0000
0600 0000 0000 0000
0700 0000 0000 0000
0800 0000 0000 0000
0900 0000 0000 0000
END

Processes

Create and manage processes

Fork()

Fork

Example:

  Fork;                                                                         # Fork  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  Test rax,rax;
  IfNz                                                                          # Parent
   {Mov rbx, rax;
    WaitPid;
    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;
    KeepFree rax;
    GetPid;                                                                     # Pid of parent as seen in parent
    Mov rcx,rax;
    PrintOutRegisterInHex rcx;
   }
  sub                                                                           # Child
   {Mov r8,rax;
    PrintOutRegisterInHex r8;
    KeepFree rax;
    GetPid;                                                                     # Child pid as seen in child
    Mov r9,rax;
    PrintOutRegisterInHex r9;
    KeepFree rax;
    GetPPid;                                                                    # Parent pid as seen in child
    Mov r10,rax;
    PrintOutRegisterInHex r10;
   };

  my $r = Assemble;

#    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;
   }

GetPid()

Get process identifier

Example:

  Fork;                                                                         # Fork

  Test rax,rax;
  IfNz                                                                          # Parent
   {Mov rbx, rax;
    WaitPid;
    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;
    KeepFree rax;

    GetPid;                                                                     # Pid of parent as seen in parent  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov rcx,rax;
    PrintOutRegisterInHex rcx;
   }
  sub                                                                           # Child
   {Mov r8,rax;
    PrintOutRegisterInHex r8;
    KeepFree rax;

    GetPid;                                                                     # Child pid as seen in child  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r9,rax;
    PrintOutRegisterInHex r9;
    KeepFree rax;
    GetPPid;                                                                    # Parent pid as seen in child
    Mov r10,rax;
    PrintOutRegisterInHex r10;
   };

  my $r = Assemble;

#    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;
   }

GetPidInHex()

Get process identifier in hex as 8 zero terminated bytes in rax

Example:

GetPidInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;

ok Assemble =~ m(rax: 00);

GetPPid()

Get parent process identifier

Example:

  Fork;                                                                         # Fork

  Test rax,rax;
  IfNz                                                                          # Parent
   {Mov rbx, rax;
    WaitPid;
    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;
    KeepFree rax;
    GetPid;                                                                     # Pid of parent as seen in parent
    Mov rcx,rax;
    PrintOutRegisterInHex rcx;
   }
  sub                                                                           # Child
   {Mov r8,rax;
    PrintOutRegisterInHex r8;
    KeepFree rax;
    GetPid;                                                                     # Child pid as seen in child
    Mov r9,rax;
    PrintOutRegisterInHex r9;
    KeepFree rax;

    GetPPid;                                                                    # Parent pid as seen in child  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    Mov r10,rax;
    PrintOutRegisterInHex r10;
   };

  my $r = Assemble;

#    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;
   }

GetUid()

Get userid of current process

Example:

GetUid;                                                                       # Userid  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;

my $r = Assemble;
ok $r =~ m(rax:( 0000){3});

WaitPid()

Wait for the pid in rax to complete

Example:

  Fork;                                                                         # Fork

  Test rax,rax;
  IfNz                                                                          # Parent
   {Mov rbx, rax;

    WaitPid;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

    PrintOutRegisterInHex rax;
    PrintOutRegisterInHex rbx;
    KeepFree rax;
    GetPid;                                                                     # Pid of parent as seen in parent
    Mov rcx,rax;
    PrintOutRegisterInHex rcx;
   }
  sub                                                                           # Child
   {Mov r8,rax;
    PrintOutRegisterInHex r8;
    KeepFree rax;
    GetPid;                                                                     # Child pid as seen in child
    Mov r9,rax;
    PrintOutRegisterInHex r9;
    KeepFree rax;
    GetPPid;                                                                    # Parent pid as seen in child
    Mov r10,rax;
    PrintOutRegisterInHex r10;
   };

  my $r = Assemble;

#    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;
   }

ReadTimeStampCounter()

Read the time stamp counter and return the time in nanoseconds in rax

Example:

for(1..10)

 {ReadTimeStampCounter;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax;
 }

my @s = split /
/, Assemble;
my @S = sort @s;
is_deeply \@s, \@S;

Stack

Manage data on the stack

Push, Pop, Peek

Generic versions of push, pop, peek

PushRR(@r)

Push registers onto the stack without tracking

   Parameter  Description
1  @r         Register

PushR(@r)

Push registers onto the stack

   Parameter  Description
1  @r         Register

Example:

  Mov rax, 0x11111111;
  Mov rbx, 0x22222222;

  PushR my @save = (rax, rbx);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  Mov rax, 0x33333333;
  PopR @save;
  PrintOutRegisterInHex rax;
  PrintOutRegisterInHex rbx;

  is_deeply Assemble,<<END;
   rax: 0000 0000 1111 1111
   rbx: 0000 0000 2222 2222
END

PopRR(@r)

Pop registers from the stack without tracking

   Parameter  Description
1  @r         Register

PopR(@r)

Pop registers from the stack

   Parameter  Description
1  @r         Register

Example:

  Mov rax, 0x11111111;
  Mov rbx, 0x22222222;
  PushR my @save = (rax, rbx);
  Mov rax, 0x33333333;

  PopR @save;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax;
  PrintOutRegisterInHex rbx;

  is_deeply Assemble,<<END;
   rax: 0000 0000 1111 1111
   rbx: 0000 0000 2222 2222
END

PeekR($r)

Peek at register on stack

   Parameter  Description
1  $r         Register

Declarations

Declare variables and structures

Structures

Declare a structure

Structure()

Create a structure addressed by a register

Structure::field($structure, $length, $comment)

Add a field of the specified length with an optional comment

   Parameter   Description
1  $structure  Structure data descriptor
2  $length     Length of data
3  $comment    Optional comment

StructureField::addr($field, $register)

Address a field in a structure by either the default register or the named register

   Parameter  Description
1  $field     Field
2  $register  Optional address register else rax

All8Structure($N)

Create a structure consisting of 8 byte fields

   Parameter  Description
1  $N         Number of variables required

Stack Frame

Declare local variables in a frame on the stack

LocalData()

Map local data

LocalData::start($local)

Start a local data area on the stack

   Parameter  Description
1  $local     Local data descriptor

LocalData::free($local)

Free a local data area on the stack

   Parameter  Description
1  $local     Local data descriptor

LocalData::variable($local, $length, $comment)

Add a local variable

   Parameter  Description
1  $local     Local data descriptor
2  $length    Length of data
3  $comment   Optional comment

LocalVariable::stack($variable)

Address a local variable on the stack

   Parameter  Description
1  $variable  Variable

LocalData::allocate8($local, @comments)

Add some 8 byte local variables and return an array of variable definitions

   Parameter  Description
1  $local     Local data descriptor
2  @comments  Optional comment

AllocateAll8OnStack($N)

Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions...)

   Parameter  Description
1  $N         Number of variables required

Memory

Allocate and print memory

PrintOutMemoryInHex()

Dump memory from the address in rax for the length in rdi

Example:

  Mov rax, 0x07654321;
  Shl rax, 32;
  Or  rax, 0x07654321;
  PushR rax;

  PrintOutRaxInHex;
  PrintOutNL;
  PrintOutRaxInReverseInHex;
  PrintOutNL;
  KeepFree rax;

  Mov rax, rsp;
  Mov rdi, 8;

  PrintOutMemoryInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutNL;
  PopR rax;
  KeepFree rax, rdi;

  Mov rax, 4096;
  PushR rax;
  Mov rax, rsp;
  Mov rdi, 8;

  PrintOutMemoryInHex;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutNL;
  PopR rax;

  is_deeply Assemble, <<END;
0765 4321 0765 4321
2143 6507 2143 6507
2143 6507 2143 6507
0010 0000 0000 0000
END

PrintOutMemoryInHexNL()

Dump memory from the address in rax for the length in rdi and then print a new line

PrintOutMemory()

Print the memory addressed by rax for a length of rdi::

Example:

Comment "Print a string from memory";
my $s = "Hello World";
Mov rax, Rs($s);
Mov rdi, length $s;

PrintOutMemory;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


ok Assemble =~ m(Hello World);

PrintOutMemoryNL()

Print the memory addressed by rax for a length of rdi followed by a new line

AllocateMemory()

Allocate the specified amount of memory via mmap and return its address

Example:

  my $N = Vq(size, 2048);
  my $q = Rs('a'..'p');

  AllocateMemory->call($N, my $address = Vq(address));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $address->dump;

  Vmovdqu8 xmm0, "[$q]";
  $address->setReg(rax);
  Vmovdqu8 "[rax]", xmm0;
  Mov rdi, 16;
  PrintOutMemory;
  PrintOutNL;

  FreeMemory->call(address => $address, size=> $N);

  ok Assemble =~ m(address: 0000.*abcdefghijklmnop)s;

  my $N = Vq(size, 4096);                                                       # Size of the initial allocation which should be one or more pages


  AllocateMemory->call($N, my $A = Vq(address));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $A->dump;

  ClearMemory->call($N, $A);

  $A->setReg(rax);
  $N->setReg(rdi);
  PrintOutMemoryInHex;

  FreeMemory->call($N, $A);

  my $r = Assemble;
  if ($r =~ m((0000.*0000))s)
   {is_deeply length($1), 9748;
   }

  my $N = 256;
  my $s = Rb 0..$N-1;

  AllocateMemory->call(Vq(size, $N), my $a = Vq(address));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  CopyMemory    ->call(Vq(source, $s), Vq(size, $N), target => $a);

  AllocateMemory->call(Vq(size, $N), my $b = Vq(address));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  CopyMemory    ->call(source => $a, target => $b, Vq(size, $N));

  $b->setReg(rax);
  Mov rdi, $N;
  PrintOutMemoryInHexNL;

  is_deeply Assemble, <<END;
0001 0203 0405 06070809 0A0B 0C0D 0E0F1011 1213 1415 16171819 1A1B 1C1D 1E1F2021 2223 2425 26272829 2A2B 2C2D 2E2F3031 3233 3435 36373839 3A3B 3C3D 3E3F4041 4243 4445 46474849 4A4B 4C4D 4E4F5051 5253 5455 56575859 5A5B 5C5D 5E5F6061 6263 6465 66676869 6A6B 6C6D 6E6F7071 7273 7475 76777879 7A7B 7C7D 7E7F8081 8283 8485 86878889 8A8B 8C8D 8E8F9091 9293 9495 96979899 9A9B 9C9D 9E9FA0A1 A2A3 A4A5 A6A7A8A9 AAAB ACAD AEAFB0B1 B2B3 B4B5 B6B7B8B9 BABB BCBD BEBFC0C1 C2C3 C4C5 C6C7C8C9 CACB CCCD CECFD0D1 D2D3 D4D5 D6D7D8D9 DADB DCDD DEDFE0E1 E2E3 E4E5 E6E7E8E9 EAEB ECED EEEFF0F1 F2F3 F4F5 F6F7F8F9 FAFB FCFD FEFF
END

FreeMemory()

Free memory

Example:

my $N = Vq(size, 4096);                                                       # Size of the initial allocation which should be one or more pages

AllocateMemory->call($N, my $A = Vq(address));
$A->dump;

ClearMemory->call($N, $A);

$A->setReg(rax);
$N->setReg(rdi);
PrintOutMemoryInHex;


FreeMemory->call($N, $A);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


my $r = Assemble;
if ($r =~ m((0000.*0000))s)
 {is_deeply length($1), 9748;
 }

ClearMemory()

Clear memory - the address of the memory is in rax, the length in rdi

Example:

my $N = Vq(size, 4096);                                                       # Size of the initial allocation which should be one or more pages

AllocateMemory->call($N, my $A = Vq(address));
$A->dump;


ClearMemory->call($N, $A);  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


$A->setReg(rax);
$N->setReg(rdi);
PrintOutMemoryInHex;

FreeMemory->call($N, $A);

my $r = Assemble;
if ($r =~ m((0000.*0000))s)
 {is_deeply length($1), 9748;
 }

CopyMemory()

Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi

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->call(Vq(source, rsi), Vq(target, rax), Vq(size, rdi));  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutMemoryInHex;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(xmm1: 0000 0000 0000 0004   0000 0003 0002 0100);
ok $r =~ m(0001 0200 0300 00000400 0000 0000 0000);

Files

Process a file

OpenRead()

Open a file, whose name is addressed by rax, for read and return the file descriptor in rax

Example:

  Mov rax, Rs($0);                                                              # File to read

  OpenRead;                                                                     # Open file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax;
  CloseFile;                                                                    # Close file
  PrintOutRegisterInHex rax;
  KeepFree rax, rdi;

  Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write
  OpenWrite;                                                                    # Open file
  CloseFile;                                                                    # Close file

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rax: 0000 0000 0000 0000
END
  ok -e $f;                                                                     # Created file
  unlink $f;

OpenWrite()

Create the file named by the terminated string addressed by rax for write

Example:

  Mov rax, Rs($0);                                                              # File to read
  OpenRead;                                                                     # Open file
  PrintOutRegisterInHex rax;
  CloseFile;                                                                    # Close file
  PrintOutRegisterInHex rax;
  KeepFree rax, rdi;

  Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write

  OpenWrite;                                                                    # Open file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  CloseFile;                                                                    # Close file

  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rax: 0000 0000 0000 0000
END
  ok -e $f;                                                                     # Created file
  unlink $f;

CloseFile()

Close the file whose descriptor is in rax

Example:

  Mov rax, Rs($0);                                                              # File to read
  OpenRead;                                                                     # Open file
  PrintOutRegisterInHex rax;

  CloseFile;                                                                    # Close file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  PrintOutRegisterInHex rax;
  KeepFree rax, rdi;

  Mov rax, Rs(my $f = "zzzTemporaryFile.txt");                                  # File to write
  OpenWrite;                                                                    # Open file

  CloseFile;                                                                    # Close file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  is_deeply Assemble, <<END;
   rax: 0000 0000 0000 0003
   rax: 0000 0000 0000 0000
END
  ok -e $f;                                                                     # Created file
  unlink $f;

StatSize()

Stat a file whose name is addressed by rax to get its size in rax

Example:

Mov rax, Rs($0);                                                              # File to stat

StatSize;                                                                     # Stat the file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex rax;

my $r = Assemble =~ 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));
 }

ReadFile()

Read a file whose name is addressed by rax into memory. The address of the mapped memory and its length are returned in registers rax,rdi

Example:

ReadFile->call(Vq(file, Rs($0)), (my $s = Vq(size)), my $a = Vq(address));    # Read file  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

$a->setReg(rax);
$s->setReg(rdi);
PrintOutMemory;                                                               # Print memory

my $r = Assemble(1 => (my $f = temporaryFile));                               # Assemble and execute
my $i = index(removeNonAsciiChars(readFile $f), removeNonAsciiChars(readFile $0));     # Output contains this file
ok $i > -1;

Short Strings

Operations on Short Strings

LoadShortStringFromMemoryToZmm2($zmm)

Load the short string addressed by rax into the zmm register with the specified number

   Parameter  Description
1  $zmm       Zmm register to load

LoadShortStringFromMemoryToZmm($zmm, $address)

Load the short string addressed by rax into the zmm register with the specified number

   Parameter  Description
1  $zmm       Zmm register to load
2  $address   Address of string in memory

Example:

my $s = Rb(3, 0x01, 0x02, 0x03);
my $t = Rb(7, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a);


LoadShortStringFromMemoryToZmm 0, $s;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


LoadShortStringFromMemoryToZmm 1, $t;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

ConcatenateShortStrings(0, 1);
PrintOutRegisterInHex xmm0;
PrintOutRegisterInHex xmm1;

my $r = Assemble;
ok $r =~ m(xmm0: 0000 0000 000A 0908   0706 0504 0302 010A);
ok $r =~ m(xmm1: 0000 0000 0000 0000   0A09 0807 0605 0407);

GetLengthOfShortString($reg, $zmm)

Get the length of the short string held in the numbered zmm register into the specified register

   Parameter  Description
1  $reg       Register to hold length
2  $zmm       Number of zmm register containing string

SetLengthOfShortString($zmm, $reg)

Set the length of the short string held in the numbered zmm register into the specified register

   Parameter  Description
1  $zmm       Number of zmm register containing string
2  $reg       Register to hold length

ConcatenateShortStrings($left, $right)

Concatenate the numbered source zmm containing a short string with the short string in the numbered target zmm.

   Parameter  Description
1  $left      Target zmm
2  $right     Source zmm

Hash functions

Hash functions

Hash()

Hash a string addressed by rax with length held in rdi and return the hash code in r15

Example:

Mov rax, "[rbp+24]";
Cstrlen;                                                                      # Length of string to hash
Mov rdi, r15;

Hash();                                                                       # Hash string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


PrintOutRegisterInHex r15;

my $e = Assemble keep=>'hash';                                                # Assemble to the specified file name

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 and $develop)                                                           # 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);
 }

Byte Strings

Operations on Byte Strings

Cstrlen()

Length of the C style string addressed by rax returning the length in r15

Example:

my $s = Rs("abcd");
Mov rax, $s;

Cstrlen;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

PrintOutRegisterInHex r15;
ok Assemble =~ m(r15: 0000 0000 0000 0004);

CreateByteString(%options)

Create an relocatable string of bytes in an arena and returns its address in rax. Optionally add a chain header so that 64 byte blocks of memory can be freed and reused within the byte string.

   Parameter  Description
1  %options   Free=>1 adds a free chain.

Example:

  my $a = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->q('aa');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
END


  my $a = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $b = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->q('aa');
  $b->q('bb');
  $a->out;
  PrintOutNL;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
bb
END


  my $a = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $b = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->q('aa');
  $a->q('AA');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAA
END


  my $a = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲


  my $b = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->q('aa');
  $b->q('bb');
  $a->q('AA');
  $b->q('BB');
  $a->q('aa');
  $b->q('bb');
  $a->out;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAAaabbBBbb
END


  my $a = CreateByteString;                                                     # Create a string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->q('ab');

  my $b = CreateByteString;                                                     # Create target byte string  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $a->bs(r15);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);


  $a->out;   PrintOutNL;                                                        # Print byte string
  $b->out;   PrintOutNL;                                                        # Print byte string
  $a->length->call($a->bs, my $sa = Vq(size)); $sa->dump;
  $a->length->call($b->bs, my $sb = Vq(size)); $sb->dump;
  $a->clear ->call($a->bs);
  $a->length->call($a->bs, my $sA = Vq(size)); $sA->dump;
  $a->length->call($b->bs, my $sB = Vq(size)); $sB->dump;

  is_deeply Assemble, <<END;                                                    # Assemble and execute
abababababababab
ababababababababababababababababababababababababababababababababababababab
size: 0000 0000 0000 0010
size: 0000 0000 0000 004A
size: 0000 0000 0000 0000
size: 0000 0000 0000 004A
END

ByteString::length($byteString)

Get the length of a byte string

   Parameter    Description
1  $byteString  Byte string descriptor

ByteString::makeReadOnly($byteString)

Make a byte string read only

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

my $s = CreateByteString;                                                     # Create a byte string
$s->q("Hello");                                                               # Write data to byte string
$s->makeReadOnly ->call($s->bs);                                              # Make byte string read only - tested above
$s->makeWriteable->call($s->bs);                                              # Make byte string writable again
$s->q(" World");                                                              # Try to write to byte string
$s->out;

ok Assemble =~ m(Hello World);

ByteString::makeWriteable($byteString)

Make a byte string writable

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

my $s = CreateByteString;                                                     # Create a byte string
$s->q("Hello");                                                               # Write data to byte string
$s->makeReadOnly ->call($s->bs);                                              # Make byte string read only - tested above
$s->makeWriteable->call($s->bs);                                              # Make byte string writable again
$s->q(" World");                                                              # Try to write to byte string
$s->out;

ok Assemble =~ m(Hello World);

ByteString::allocate($byteString)

Allocate the amount of space indicated in rdi in the byte string addressed by rax and return the offset of the allocation in the arena in rdi

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $s = CreateByteString;                                                     # Create a byte string
  $s->allocate->call($s->bs, Vq(size, 0x20), my $o1 = Vq(offset));              # Allocate space wanted
  $s->allocate->call($s->bs, Vq(size, 0x30), my $o2 = Vq(offset));
  $s->allocate->call($s->bs, Vq(size, 0x10), my $o3 = Vq(offset));
  $o1->dump;
  $o2->dump;
  $o3->dump;

  is_deeply Assemble, <<END;
offset: 0000 0000 0000 0018
offset: 0000 0000 0000 0038
offset: 0000 0000 0000 0068
END

ByteString::m($byteString)

Append the content with length rdi addressed by rsi to the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string descriptor

ByteString::q($byteString, $string)

Append a constant string to the byte string

   Parameter    Description
1  $byteString  Byte string descriptor
2  $string      String

Example:

my $s = CreateByteString;                                                     # Create a string
$s->read->call($s->bs, Vq(file, Rs($0)));
$s->out;

my $r = Assemble(1 => my $f = temporaryFile);
is_deeply fileMd5Sum($f), fileMd5Sum($0);                                     # Output contains this file
unlink $f;

ByteString::ql($byteString, $const)

Append a quoted string containing new line characters to the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string
2  $const       Constant

Example:

  my $s = CreateByteString;                                                     # Create a string
  $s->ql(<<END);                                                                # Write code to execute
#!/usr/bin/bash
whoami
ls -la
pwd
END
  $s->write         ->call($s->bs, my $f = Vq('file', Rs("zzz.sh")));           # Write code to a file
  executeFileViaBash->call($f);                                                 # Execute the file
  unlinkFile        ->call($f);                                                 # Delete the file

  my $u = qx(whoami); chomp($u);
  ok Assemble(emulator=>0) =~ m($u);                                            # The Intel Software Development Emulator is way too slow on these operations.

ByteString::char($byteString, $char)

Append a character expressed as a decimal number to the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string descriptor
2  $char        Number of character to be appended

ByteString::nl($byteString)

Append a new line to the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $a = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->out;
  PrintOutNL;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
bb
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->q('AA');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAA
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->q('AA');
  $b->q('BB');
  $a->q('aa');
  $b->q('bb');
  $a->out;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAAaabbBBbb
END

  my $a = CreateByteString;                                                     # Create a string
  $a->q('ab');
  my $b = CreateByteString;                                                     # Create target byte string
  $a->bs(r15);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);


  $a->out;   PrintOutNL;                                                        # Print byte string
  $b->out;   PrintOutNL;                                                        # Print byte string
  $a->length->call($a->bs, my $sa = Vq(size)); $sa->dump;
  $a->length->call($b->bs, my $sb = Vq(size)); $sb->dump;
  $a->clear ->call($a->bs);
  $a->length->call($a->bs, my $sA = Vq(size)); $sA->dump;
  $a->length->call($b->bs, my $sB = Vq(size)); $sB->dump;

  is_deeply Assemble, <<END;                                                    # Assemble and execute
abababababababab
ababababababababababababababababababababababababababababababababababababab
size: 0000 0000 0000 0010
size: 0000 0000 0000 004A
size: 0000 0000 0000 0000
size: 0000 0000 0000 004A
END

  my $s = CreateByteString;
  $s->q("A");
  $s->nl;
  $s->q("B");
  $s->out;
  PrintOutNL;

  is_deeply Assemble, <<END;
A
B
END

ByteString::z($byteString)

Append a trailing zero to the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

my $s = CreateByteString;                                                     # Create a string
$s->read->call($s->bs, Vq(file, Rs($0)));
$s->out;

my $r = Assemble(1 => my $f = temporaryFile);
is_deeply fileMd5Sum($f), fileMd5Sum($0);                                     # Output contains this file
unlink $f;

ByteString::rdiInHex()

Add the content of register rdi in hexadecimal in big endian notation to a byte string

Example:

my $s = CreateByteString;                                                     # Create a string
Mov rdi, 0x88776655;
Shl rdi, 32;
Or  rdi, 0x44332211;

$s->rdiInHex;                                                                 # Append a constant to the byte string
$s->out;

ok Assemble =~ m(8877665544332211);

ByteString::append($byteString)

Append one byte string to another

   Parameter    Description
1  $byteString  Byte string descriptor

ByteString::clear($byteString)

Clear the byte string addressed by rax

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $a = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->out;
  PrintOutNL;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
bb
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->q('AA');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAA
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->q('AA');
  $b->q('BB');
  $a->q('aa');
  $b->q('bb');
  $a->out;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAAaabbBBbb
END

  my $a = CreateByteString;                                                     # Create a string
  $a->q('ab');
  my $b = CreateByteString;                                                     # Create target byte string
  $a->bs(r15);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);


  $a->out;   PrintOutNL;                                                        # Print byte string
  $b->out;   PrintOutNL;                                                        # Print byte string
  $a->length->call($a->bs, my $sa = Vq(size)); $sa->dump;
  $a->length->call($b->bs, my $sb = Vq(size)); $sb->dump;
  $a->clear ->call($a->bs);
  $a->length->call($a->bs, my $sA = Vq(size)); $sA->dump;
  $a->length->call($b->bs, my $sB = Vq(size)); $sB->dump;

  is_deeply Assemble, <<END;                                                    # Assemble and execute
abababababababab
ababababababababababababababababababababababababababababababababababababab
size: 0000 0000 0000 0010
size: 0000 0000 0000 004A
size: 0000 0000 0000 0000
size: 0000 0000 0000 004A
END

ByteString::write($byteString)

Write the content in a byte string addressed by rax to a temporary file and replace the byte string content with the name of the temporary file

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $s = CreateByteString;                                                     # Create a string
  $s->ql(<<END);                                                                # Write code to execute
#!/usr/bin/bash
whoami
ls -la
pwd
END
  $s->write         ->call($s->bs, my $f = Vq('file', Rs("zzz.sh")));           # Write code to a file
  executeFileViaBash->call($f);                                                 # Execute the file
  unlinkFile        ->call($f);                                                 # Delete the file

  my $u = qx(whoami); chomp($u);
  ok Assemble(emulator=>0) =~ m($u);                                            # The Intel Software Development Emulator is way too slow on these operations.

ByteString::read($byteString)

Read the named file (terminated with a zero byte) and place it into the named byte string.

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

my $s = CreateByteString;                                                     # Create a string
$s->read->call($s->bs, Vq(file, Rs($0)));
$s->out;

my $r = Assemble(1 => my $f = temporaryFile);
is_deeply fileMd5Sum($f), fileMd5Sum($0);                                     # Output contains this file
unlink $f;

ByteString::out($byteString)

Print the specified byte string addressed by rax on sysout

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $a = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->out;
  PrintOutNL;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aa
bb
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $a->q('AA');
  $a->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAA
END

  my $a = CreateByteString;                                                     # Create a string
  my $b = CreateByteString;                                                     # Create a string
  $a->q('aa');
  $b->q('bb');
  $a->q('AA');
  $b->q('BB');
  $a->q('aa');
  $b->q('bb');
  $a->out;
  $b->out;
  PrintOutNL;
  is_deeply Assemble, <<END;                                                    # Assemble and execute
aaAAaabbBBbb
END

  my $a = CreateByteString;                                                     # Create a string
  $a->q('ab');
  my $b = CreateByteString;                                                     # Create target byte string
  $a->bs(r15);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$a->bs, source=>$b->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);
  $a->append->call(target=>$b->bs, source=>$a->bs);


  $a->out;   PrintOutNL;                                                        # Print byte string
  $b->out;   PrintOutNL;                                                        # Print byte string
  $a->length->call($a->bs, my $sa = Vq(size)); $sa->dump;
  $a->length->call($b->bs, my $sb = Vq(size)); $sb->dump;
  $a->clear ->call($a->bs);
  $a->length->call($a->bs, my $sA = Vq(size)); $sA->dump;
  $a->length->call($b->bs, my $sB = Vq(size)); $sB->dump;

  is_deeply Assemble, <<END;                                                    # Assemble and execute
abababababababab
ababababababababababababababababababababababababababababababababababababab
size: 0000 0000 0000 0010
size: 0000 0000 0000 004A
size: 0000 0000 0000 0000
size: 0000 0000 0000 004A
END

  my $s = CreateByteString;                                                     # Create a string
  $s->ql(<<END);                                                                # Write code to execute
#!/usr/bin/bash
whoami
ls -la
pwd
END
  $s->write         ->call($s->bs, my $f = Vq('file', Rs("zzz.sh")));           # Write code to a file
  executeFileViaBash->call($f);                                                 # Execute the file
  unlinkFile        ->call($f);                                                 # Delete the file

  my $u = qx(whoami); chomp($u);
  ok Assemble(emulator=>0) =~ m($u);                                            # The Intel Software Development Emulator is way too slow on these operations.

executeFileViaBash()

Execute the file named in the byte string addressed by rax with bash

unlinkFile()

Unlink the named file

ByteString::dump($byteString)

Dump details of a byte string

   Parameter    Description
1  $byteString  Byte string descriptor

Example:

  my $t = GenTree(2,2);                                                         # Tree description
  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2

    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root
    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion
   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3

    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root
    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion
   }

  cxr
   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();
    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};
   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

Block Strings

Strings made from zmm sized blocks of text

ByteString::CreateBlockString($byteString)

Create a string from a doubly link linked list of 64 byte blocks linked via 4 byte offsets in the byte string addressed by rax and return its descriptor

   Parameter    Description
1  $byteString  Byte string description

BlockString::allocBlock($blockString, $bsa)

Allocate a block to hold a zmm register in the specified byte string and return the offset of the block in a variable

   Parameter     Description
1  $blockString  Block string descriptor
2  $bsa          Variable containing address of underlying byte string

BlockString::getBlockLengthInZmm($blockString, $zmm)

Get the block length of the numbered zmm and return it in a variable

   Parameter     Description
1  $blockString  Block string descriptor
2  $zmm          Number of zmm register

BlockString::setBlockLengthInZmm($blockString, $length, $zmm)

Set the block length of the numbered zmm to the specified length

   Parameter     Description
1  $blockString  Block string descriptor
2  $length       Length as a variable
3  $zmm          Number of zmm register

BlockString::getBlock($blockString, $bsa, $block, $zmm)

Get the block with the specified offset in the specified block string and return it in the numbered zmm

   Parameter     Description
1  $blockString  Block string descriptor
2  $bsa          Byte string variable
3  $block        Offset of the block as a variable
4  $zmm          Number of zmm register to contain block

BlockString::putBlock($blockString, $bsa, $block, $zmm)

Write the numbered zmm to the block at the specified offset in the specified byte string

   Parameter     Description
1  $blockString  Block string descriptor
2  $bsa          Byte string variable
3  $block        Block in byte string
4  $zmm          Content variable

BlockString::getNextAndPrevBlockOffsetFromZmm($blockString, $zmm)

Get the offsets of the next and previous blocks as variables from the specified zmm

   Parameter     Description
1  $blockString  Block string descriptor
2  $zmm          Zmm containing block

BlockString::putNextandPrevBlockOffsetIntoZmm($blockString, $zmm, $next, $prev)

Save next and prev offsets into a zmm representing a block

   Parameter     Description
1  $blockString  Block string descriptor
2  $zmm          Zmm containing block
3  $next         Next offset as a variable
4  $prev         Prev offset as a variable

BlockString::dump($blockString)

Dump a block string to sysout

   Parameter     Description
1  $blockString  Block string descriptor

BlockString::appendSub($blockString, $first, $source, $length, $bsa)

Append the specified content in memory to the specified block string

   Parameter     Description
1  $blockString  Block string descriptor
2  $first        Variable containing offset of first block
3  $source       Variable addressing source
4  $length       Variable containing length of source
5  $bsa          Variable containing byte string address

BlockString::append($blockString, $source, $length)

Append the specified content in memory to the specified block string

   Parameter     Description
1  $blockString  Block string descriptor
2  $source       Variable addressing source
3  $length       Variable containing length of source

Tree

Tree operations

GenTree($keyLength, $dataLength)

Generate a set of routines to manage a tree held in a byte string with key and data fields of specified widths. Allocate a byte string to contain the tree, return its address in xmm0=(0, tree).

   Parameter    Description
1  $keyLength   Fixed key length in bytes
2  $dataLength  Fixed data length in bytes

Example:

  my $t = GenTree(2,2);                                                         # Tree description  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

  $t->node->();                                                                 # Root
  Movdqa xmm1, xmm0;                                                            # Root is in xmm1

  if (1)                                                                        # New left node
   {$t->node->();                                                               # Node in xmm0
    Movdqa xmm2, xmm0;                                                          # Left is in xmm2

    cxr {$t->insertLeft->()} 1,2;                                               # Insert left under root
    cxr {$t->dump->("Left")} 2;                                                 # Left node after insertion
   }

  if (1)                                                                        # New right node in xmm0
   {$t->node->();
    Movdqa xmm3, xmm0;                                                          # Right is in xmm3

    cxr {$t->insertRight->()} 1,3;                                              # Insert left under root
    cxr {$t->dump->("Right")} 3;                                                # Right node after insertion
   }

  cxr
   {$t->dump->("Root");                                                         # Root node after insertions
    $t->isRoot->();
    IfNz {PrintOutStringNL "root"} sub {PrintOutStringNL "NOT root"};
   } 1;

  PushRR xmm0;                                                                  # Dump underlying  byte string
  PopRR rdi, rax;
  $t->byteString->dump;

  Exit;                                                                         # Return to operating system

  is_deeply Assemble, <<END;                                                    # Test tree so produced
Left
ArenaTreeNode at: 0000 0000 0000 00B0
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Right
ArenaTreeNode at: 0000 0000 0000 0150
   up: 0000 0000 0000 0010
 left: 0000 0000 0000 0000
right: 0000 0000 0000 0000
Root
ArenaTreeNode at: 0000 0000 0000 0010
   up: 0000 0000 0000 0000
 left: 0000 0000 0000 00B0
right: 0000 0000 0000 0150
root
Byte String
  Size: 0000 0000 0000 1000
  Used: 0000 0000 0000 01E0
END

Assemble

Assemble generated code

Start()

Initialize the assembler

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

LocateIntelEmulator()

Assemble the generated code

Assemble(%options)

Assemble the generated code

   Parameter  Description
1  %options   Options

Example:

  PrintOutStringNL "Hello World";
  PrintErrStringNL "Hello World";


  is_deeply Assemble, <<END;  # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲

Hello World
Hello World
END

BlockString Definition

Block string definition

Output fields

address

Variable addressing first block in string

byteString

Bytes string definition

length

Variable containing maximum length in a block

Location of links in bytes in zmm

next

Location of next offset in block in bytes

offset

Size of an offset is size of eax

prev

Location of prev offset in block in bytes

size

Size of a block == size of a zmm

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.

Replaceable Attribute List

Pi32 Pi64

Pi32

Pi as a 32 bit float

Pi64

Pi as a 64 bit float

Private Methods

Label()

Create a unique label

Dbwdq($s, @d)

Layout data

   Parameter  Description
1  $s         Element size
2  @d         Data to be laid out

Rbwdq($s, @d)

Layout data

   Parameter  Description
1  $s         Element size
2  @d         Data to be laid out

hexTranslateTable()

Create/address a hex translate table and return its label

PrintOutRipInHex()

Print the instruction pointer in hex

PrintOutRflagsInHex()

Print the flags register in hex

Variable::confirmIsMemory($memory, $address, $length)

Check that variable describes allocated memory and optionally load registers with its length and size

   Parameter  Description
1  $memory    Variable describing memory as returned by Allocate Memory
2  $address   Register to contain address
3  $length    Register to contain size

ByteString::updateSpace($byteString)

Make sure that the byte string addressed by rax has enough space to accommodate content of length rdi

   Parameter    Description
1  $byteString  Byte string descriptor

removeNonAsciiChars($string)

Return a copy of the specified string with all the non ascii characters removed

   Parameter  Description
1  $string    String

totalBytesAssembled()

Total size in bytes of all files assembled during testing

Index

1 All8Structure - Create a structure consisting of 8 byte fields

2 AllocateAll8OnStack - Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions.

3 AllocateMemory - Allocate the specified amount of memory via mmap and return its address

4 Assemble - Assemble the generated code

5 BlockString::allocBlock - Allocate a block to hold a zmm register in the specified byte string and return the offset of the block in a variable

6 BlockString::append - Append the specified content in memory to the specified block string

7 BlockString::appendSub - Append the specified content in memory to the specified block string

8 BlockString::dump - Dump a block string to sysout

9 BlockString::getBlock - Get the block with the specified offset in the specified block string and return it in the numbered zmm

10 BlockString::getBlockLengthInZmm - Get the block length of the numbered zmm and return it in a variable

11 BlockString::getNextAndPrevBlockOffsetFromZmm - Get the offsets of the next and previous blocks as variables from the specified zmm

12 BlockString::putBlock - Write the numbered zmm to the block at the specified offset in the specified byte string

13 BlockString::putNextandPrevBlockOffsetIntoZmm - Save next and prev offsets into a zmm representing a block

14 BlockString::setBlockLengthInZmm - Set the block length of the numbered zmm to the specified length

15 ByteString::allocate - Allocate the amount of space indicated in rdi in the byte string addressed by rax and return the offset of the allocation in the arena in rdi

16 ByteString::append - Append one byte string to another

17 ByteString::char - Append a character expressed as a decimal number to the byte string addressed by rax

18 ByteString::clear - Clear the byte string addressed by rax

19 ByteString::CreateBlockString - Create a string from a doubly link linked list of 64 byte blocks linked via 4 byte offsets in the byte string addressed by rax and return its descriptor

20 ByteString::dump - Dump details of a byte string

21 ByteString::length - Get the length of a byte string

22 ByteString::m - Append the content with length rdi addressed by rsi to the byte string addressed by rax

23 ByteString::makeReadOnly - Make a byte string read only

24 ByteString::makeWriteable - Make a byte string writable

25 ByteString::nl - Append a new line to the byte string addressed by rax

26 ByteString::out - Print the specified byte string addressed by rax on sysout

27 ByteString::q - Append a constant string to the byte string

28 ByteString::ql - Append a quoted string containing new line characters to the byte string addressed by rax

29 ByteString::rdiInHex - Add the content of register rdi in hexadecimal in big endian notation to a byte string

30 ByteString::read - Read the named file (terminated with a zero byte) and place it into the named byte string.

31 ByteString::updateSpace - Make sure that the byte string addressed by rax has enough space to accommodate content of length rdi

32 ByteString::write - Write the content in a byte string addressed by rax to a temporary file and replace the byte string content with the name of the temporary file

33 ByteString::z - Append a trailing zero to the byte string addressed by rax

34 ClearMemory - Clear memory - the address of the memory is in rax, the length in rdi

35 ClearRegisters - Clear registers by setting them to zero

36 ClearZF - Clear the zero flag

37 CloseFile - Close the file whose descriptor is in rax

38 Comment - Insert a comment into the assembly code

39 ConcatenateShortStrings - Concatenate the numbered source zmm containing a short string with the short string in the numbered target zmm.

40 Copy - Copy the source to the target register

41 CopyMemory - Copy memory, the target is addressed by rax, the length is in rdi, the source is addressed by rsi

42 cr - Call a subroutine with a reordering of the registers.

43 CreateByteString - Create an relocatable string of bytes in an arena and returns its address in rax.

44 Cstrlen - Length of the C style string addressed by rax returning the length in r15

45 cxr - Call a subroutine with a reordering of the xmm registers.

46 Db - Layout bytes in the data segment and return their label

47 Dbwdq - Layout data

48 DComment - Insert a comment into the data segment

49 Dd - Layout double words in the data segment and return their label

50 Decrement - Decrement the specified register

51 Dq - Layout quad words in the data segment and return their label

52 Ds - Layout bytes in memory and return their label

53 Dw - Layout words in the data segment and return their label

54 executeFileViaBash - Execute the file named in the byte string addressed by rax with bash

55 Exit - Exit with the specified return code or zero if no return code supplied.

56 Float32 - 32 bit float

57 Float64 - 64 bit float

58 For - For - iterate the body as long as register is less than limit incrementing by increment each time

59 ForEver - Iterate for ever

60 ForIn - For - iterate the body as long as register plus increment is less than than limit incrementing by increment each time

61 Fork - Fork

62 FreeMemory - Free memory

63 GenTree - Generate a set of routines to manage a tree held in a byte string with key and data fields of specified widths.

64 getBFromZmmAsVariable - Get the byte from the numbered zmm register and return it in a variable

65 getBwdqFromZmmAsVariable - Get the numbered byte|word|double word|quad word from the numbered zmm register and return it in a variable

66 getDFromZmmAsVariable - Get the double word from the numbered zmm register and return it in a variable

67 GetLengthOfShortString - Get the length of the short string held in the numbered zmm register into the specified register

68 GetPid - Get process identifier

69 GetPidInHex - Get process identifier in hex as 8 zero terminated bytes in rax

70 GetPPid - Get parent process identifier

71 getQFromZmmAsVariable - Get the quad word from the numbered zmm register and return it in a variable

72 GetUid - Get userid of current process

73 getWFromZmmAsVariable - Get the word from the numbered zmm register and return it in a variable

74 Hash - Hash a string addressed by rax with length held in rdi and return the hash code in r15

75 hexTranslateTable - Create/address a hex translate table and return its label

76 If - If

77 IfEq - If equal execute the then body else the else body

78 IfGe - If greater than or equal execute the then body else the else body

79 IfGt - If greater than execute the then body else the else body

80 IfLe - If less than or equal execute the then body else the else body

81 IfLt - If less than execute the then body else the else body

82 IfNe - If not equal execute the then body else the else body

83 IfNz - If not zero execute the then body else the else body

84 Increment - Increment the specified register

85 InsertIntoXyz - Shift and insert the specified word, double, quad from rax or the contents of xmm0 into the specified xyz register at the specified position shifting data above it to the left towards higher order bytes.

86 Keep - Mark free registers so that they are not updated until we Free them or complain if the register is already in use.

87 KeepFree - Free registers so that they can be reused

88 KeepPop - Reset the status of the specified registers to the status quo ante the last push

89 KeepPush - Push the current status of the specified registers and then mark them as free

90 KeepReturn - Pop the specified register and mark it as in use to effect a subroutine return with this register.

91 KeepSet - Confirm that the specified registers are in use

92 Label - Create a unique label

93 LoadShortStringFromMemoryToZmm - Load the short string addressed by rax into the zmm register with the specified number

94 LoadShortStringFromMemoryToZmm2 - Load the short string addressed by rax into the zmm register with the specified number

95 LoadTargetZmmFromSourceZmm - Load bytes into the numbered target zmm register at a register specified offset with source bytes from a numbered source zmm register at a specified register offset for a specified register length.

96 LoadZmmFromMemory - Load bytes into the numbered target zmm register at a register specified offset with source bytes from memory addressed by a specified register for a specified register length from memory addressed by a specified register.

97 LocalData - Map local data

98 LocalData::allocate8 - Add some 8 byte local variables and return an array of variable definitions

99 LocalData::free - Free a local data area on the stack

100 LocalData::start - Start a local data area on the stack

101 LocalData::variable - Add a local variable

102 LocalVariable::stack - Address a local variable on the stack

103 LocateIntelEmulator - Assemble the generated code

104 Macro - Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub

105 MaximumOfTwoRegisters - Return in the specified register the value in the second register if it is greater than the value in the first register

106 MinimumOfTwoRegisters - Return in the specified register the value in the second register if it is less than the value in the first register

107 Minus - Subtract the third operand from the second operand and place the result in the first operand

108 Nasm::X86::Sub::call - Call a sub passing it some parameters

109 OpenRead - Open a file, whose name is addressed by rax, for read and return the file descriptor in rax

110 OpenWrite - Create the file named by the terminated string addressed by rax for write

111 PeekR - Peek at register on stack

112 Plus - Add the last operands and place the result in the first operand

113 PopR - Pop registers from the stack

114 PopRR - Pop registers from the stack without tracking

115 PrintErrNL - Print a new line to stderr

116 PrintErrString - Print a constant string to stderr.

117 PrintErrStringNL - Print a new line to stderr

118 PrintOutMemory - Print the memory addressed by rax for a length of rdi::

119 PrintOutMemoryInHex - Dump memory from the address in rax for the length in rdi

120 PrintOutMemoryInHexNL - Dump memory from the address in rax for the length in rdi and then print a new line

121 PrintOutMemoryNL - Print the memory addressed by rax for a length of rdi followed by a new line

122 PrintOutNL - Print a new line to stdout

123 PrintOutRaxInHex - Write the content of register rax to stderr in hexadecimal in big endian notation

124 PrintOutRaxInReverseInHex - Write the content of register rax to stderr in hexadecimal in little endian notation

125 PrintOutRegisterInHex - Print any register as a hex string

126 PrintOutRegistersInHex - Print the general purpose registers in hex

127 PrintOutRflagsInHex - Print the flags register in hex

128 PrintOutRipInHex - Print the instruction pointer in hex

129 PrintOutString - Print a constant string to sysout.

130 PrintOutStringNL - Print a constant string to sysout followed by new line

131 PrintOutZF - Print the zero flag without disturbing it

132 PushR - Push registers onto the stack

133 PushRR - Push registers onto the stack without tracking

134 Rb - Layout bytes in the data segment and return their label

135 Rbwdq - Layout data

136 RComment - Insert a comment into the read only data segment

137 Rd - Layout double words in the data segment and return their label

138 ReadFile - Read a file whose name is addressed by rax into memory.

139 ReadTimeStampCounter - Read the time stamp counter and return the time in nanoseconds in rax

140 RegisterSize - Return the size of a register

141 removeNonAsciiChars - Return a copy of the specified string with all the non ascii characters removed

142 ReorderSyscallRegisters - Map the list of registers provided to the 64 bit system call sequence

143 ReorderXmmRegisters - Map the list of xmm registers provided to 0-31

144 RestoreFirstFour - Restore the first 4 parameter registers

145 RestoreFirstFourExceptRax - Restore the first 4 parameter registers except rax so it can return its value

146 RestoreFirstFourExceptRaxAndRdi - Restore the first 4 parameter registers except rax and rdi so we can return a pair of values

147 RestoreFirstSeven - Restore the first 7 parameter registers

148 RestoreFirstSevenExceptRax - Restore the first 7 parameter registers except rax which is being used to return the result

149 RestoreFirstSevenExceptRaxAndRdi - Restore the first 7 parameter registers except rax and rdi which are being used to return the results

150 Rq - Layout quad words in the data segment and return their label

151 Rs - Layout bytes in read only memory and return their label

152 Rw - Layout words in the data segment and return their label

153 SaveFirstFour - Save the first 4 parameter registers making any parameter registers read only

154 SaveFirstSeven - Save the first 7 parameter registers

155 Scope - Create and stack a new scope and continue with it as the current scope

156 Scope::contains - Check that the named parent scope contains the specified child scope.

157 Scope::currentlyVisible - Check that the named parent scope is currently visible

158 ScopeEnd - End the current scope and continue with the containing parent scope

159 SetLabel - Set a label in the code section

160 SetLengthOfShortString - Set the length of the short string held in the numbered zmm register into the specified register

161 SetMaskRegister - Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere

162 SetRegisterToMinusOne - Set the specified register to -1

163 SetZF - Set the zero flag

164 Start - Initialize the assembler

165 StatSize - Stat a file whose name is addressed by rax to get its size in rax

166 Structure - Create a structure addressed by a register

167 Structure::field - Add a field of the specified length with an optional comment

168 StructureField::addr - Address a field in a structure by either the default register or the named register

169 Subroutine - Create a subroutine that can be called in assembler code

170 totalBytesAssembled - Total size in bytes of all files assembled during testing

171 unlinkFile - Unlink the named file

172 UnReorderSyscallRegisters - Recover the initial values in registers that were reordered

173 UnReorderXmmRegisters - Recover the initial values in the xmm registers that were reordered

174 Variable - Create a new variable with the specified size and name initialized via an expression

175 Variable::add - Add the right hand variable to the left hand variable and return the result as a new variable

176 Variable::address - Get the address of a variable with an optional offset

177 Variable::and - And two variables

178 Variable::arithmetic - Return a variable containing the result of an arithmetic operation on the left hand and right hand side variables

179 Variable::assign - Assign to the left hand side the value of the right hand side

180 Variable::boolean - Combine the left hand variable with the right hand variable via a boolean operator

181 Variable::clearMemory - Clear the memory described in this variable

182 Variable::confirmIsMemory - Check that variable describes allocated memory and optionally load registers with its length and size

183 Variable::copy - Copy one variable into another

184 Variable::copyAddress - Copy a reference to a variable

185 Variable::copyMemoryFrom - Copy from one block of memory to another

186 Variable::copyRef - Copy one variable into an referenced variable

187 Variable::debug - Dump the value of a variable on stdout with an indication of where the dump came from

188 Variable::dec - Decrement a variable

189 Variable::divide - Divide the left hand variable by the right hand variable and return the result as a new variable

190 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

191 Variable::dump - Dump the value of a variable on stdout

192 Variable::eq - Check whether the left hand variable is equal to the right hand variable

193 Variable::equals - Equals operator

194 Variable::for - Iterate the body from 0 limit times.

195 Variable::freeMemory - Free the memory described in this variable

196 Variable::ge - Check whether the left hand variable is greater than or equal to the right hand variable

197 Variable::getReg - Load the variable from the named registers

198 Variable::gt - Check whether the left hand variable is greater than the right hand variable

199 Variable::inc - Increment a variable

200 Variable::incDec - Increment or decrement a variable

201 Variable::isRef - Check whether the specified variable is a reference to another variable

202 Variable::le - Check whether the left hand variable is less than or equal to the right hand variable

203 Variable::loadZmm - Load bytes from the memory addressed by the specified source variable into the numbered zmm register.

204 Variable::lt - Check whether the left hand variable is less than the right hand variable

205 Variable::max - Maximum of two variables

206 Variable::min - Minimum of two variables

207 Variable::minusAssign - Implement minus and assign

208 Variable::mod - Divide the left hand variable by the right hand variable and return the remainder as a new variable

209 Variable::ne - Check whether the left hand variable is not equal to the right hand variable

210 Variable::or - Or two variables

211 Variable::plusAssign - Implement plus and assign

212 Variable::print - Write the value of a variable on stdout

213 Variable::printOutMemoryInHex - Print allocated memory in hex

214 Variable::putBIntoZmm - Place the value of the content variable at the byte in the numbered zmm register

215 Variable::putBwdqIntoZmm - Place the value of the content variable at the byte|word|double word|quad word in the numbered zmm register

216 Variable::putDIntoZmm - Place the value of the content variable at the double word in the numbered zmm register

217 Variable::putQIntoZmm - Place the value of the content variable at the quad word in the numbered zmm register

218 Variable::putWIntoZmm - Place the value of the content variable at the word in the numbered zmm register

219 Variable::saveZmm - Save bytes into the memory addressed by the target variable from the numbered zmm register.

220 Variable::setMask - Set the mask register to ones starting at the specified position for the specified length and zeroes elsewhere

221 Variable::setReg - Set the named registers from the content of the variable

222 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

223 Variable::str - The name of the variable

224 Variable::sub - Subtract the right hand variable from the left hand variable and return the result as a new variable

225 Variable::times - Multiply the left hand variable by the right hand variable and return the result as a new variable

226 Vb - Define a byte variable

227 Vd - Define a double word variable

228 Vq - Define a quad variable

229 Vr - Define a reference variable

230 Vw - Define a word variable

231 Vx - Define an xmm variable

232 VxyzInit - Initialize an xyz register from general purpose registers

233 Vy - Define an ymm variable

234 Vz - Define an zmm variable

235 WaitPid - Wait for the pid in rax to complete

Installation

This module is written in 100% Pure Perl and, thus, it is easy to read, comprehend, use, modify and install via cpan:

sudo cpan install Nasm::X86

Author

philiprbrenan@gmail.com

http://www.appaapps.com

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.