Generate and run X86-64 Advanced Vector Extensions assembler programs using NASM - the Netwide Assember and Perl

Test

This module generates and runs Intel X86-64 Advanced Vector Extensions assembler programs using Perl as a powerful macro preprocessor for NASM - the Netwide Assember. It contains useful methods to debug programs during development making this system an ideal development environment for any-one who wants to learn how to program quickly with Intel assembler code.

Full documentation is available on CPAN.

The GitHub Action in this repo shows how to install NASM - the Netwide Assember and the Intel Software Development Emulator used to assemble and then run the programs generated by this module.

This module provides dynamic strings, arrays, trees and quarks in arenas: every such data structure is process friendly because arenas are completely relocatable and so can be easily moved between different processes via a file or a socket or reused via shared memory.

Please feel free to join in with this interesting project - we need all the help we can get.

Useful links

Read a character and print it out from assembly code using NASM - the Netwide Assember and Perl:

Read a character from stdin and place it in rax:

``` for(1..4) {ReadChar; PrintOutRegisterInHex rax; }

Assemble keep => "readChar";

my $r = qx(echo "ABCD" | ./readChar); is_deeply $r, <

Read a file and print it out from assembly code using NASM - the Netwide Assember and Perl:

Read this file and print it out:

``` use Nasm::X86 qw(:all);

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

```

Call functions in Libc from assembly code using NASM - the Netwide Assember and Perl:

Call C functions by naming them as external and including their library:

``` my $format = Rs "Hello %s\n"; my $data = Rs "World";

Extern qw(printf exit malloc strcpy); Link 'c';

CallC 'malloc', length($format)+1; Mov r15, rax; CallC 'strcpy', r15, $format; CallC 'printf', r15, $data; CallC 'exit', 0;

ok Assemble eq => <

Print numbers in decimal from assembly code using NASM - the Netwide Assember and Perl:

Debug your programs with powerful print statements:

``` Mov rax, 0x2a; PrintOutRaxInDecNL;

ok Assemble eq => <

Avx512 instructions from assembly code using NASM - the Netwide Assember and Perl:

Use Advanced Vector Extensions instructions to compare 64 bytes at a time using the 512 bit wide zmm registers from NASM - the Netwide Assember and Perl: ``` my $P = "2F"; # Value to test for my $l = Rb 0; Rb $_ for 1..RegisterSize zmm0; # The numbers 0..63 Vmovdqu8 zmm0, "[$l]"; # Load data to test PrintOutRegisterInHex zmm0;

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 [split //, Assemble], [split //, <

Create a library from assembly code using NASM - the Netwide Assember and Perl:

Create a library with three routines in it and save the library in a file: ``` my $library = CreateLibrary # Library definition (subroutines => # Sub routines in libray {inc => sub {Inc rax}, # Increment rax dup => sub {Shl rax, 1}, # Double rax put => sub {PrintOutRaxInDecNL}, # Print rax in decimal }, file => q(library), );

```

Reuse the code in the library in another assembly: ``` my ($dup, $inc, $put) = $library->load; # Load the library into memory

Mov rax, 1; &$put; &$inc; &$put; # Use the subroutines from the library &$dup; &$put; &$dup; &$put; &$inc; &$put;

ok Assemble eq => <

Dynamic string held in an arena from assembly code using NASM - the Netwide Assember and Perl:

Create a dynamic string within an arena and add some content to it:

``` my $s = Rb(0..255); my $A = CreateArena; my $S = $A->CreateString;

$S->append(V(source, $s), K(size, 256)); $S->len->outNL; $S->clear;

$S->append(V(source, $s), K(size, 16)); $S->len->outNL; $S->dump;

ok Assemble(debug => 0, eq => <

END ```

Dynamic array held in an arena from assembly code using NASM - the Netwide Assember and Perl:

Create a dynamic array within an arena, push some content on to it then pop it off again: ``` my $N = 15; my $A = CreateArena; my $a = $A->CreateArray;

$a->push(V(element, $_)) for 1..$N;

K(loop, $N)->for(sub {my ($start, $end, $next) = @_; my $l = $a->size; If $l == 0, Then {Jmp $end}; $a->pop(my $e = V(element)); $e->outNL; });

ok Assemble(debug => 0, eq => <

Create a multi way tree in an arena using SIMD instructions from assembly code using NASM - the Netwide Assember and Perl:

Create a multiway tree as in L using B instructions and iterate through it:

``` my $N = 12; my $b = CreateArena; # Resizable memory block my $t = $b->CreateTree; # Multi way tree in memory block

K(count, $N)->for(sub # Add some entries to the tree {my ($index, $start, $next, $end) = @_; my $k = $index + 1; $t->insert($k, $k + 0x100); $t->insert($k + $N, $k + 0x200); });

$t->by(sub # Iterate through the tree {my ($iter, $end) = @_; $iter->key ->out('key: '); $iter->data->out(' data: '); $iter->tree->depth($iter->node, my $D = V(depth));

$t->find($iter->key);
$t->found->out(' found: '); $t->data->out(' data: '); $D->outNL(' depth: ');

});

$t->find(K(key, 0xffff)); $t->found->outNL('Found: '); # Find some entries $t->find(K(key, 0xd)); $t->found->outNL('Found: ');

If ($t->found, Then {$t->data->outNL("Data : "); });

ok Assemble(debug => 0, eq => <

Quarks held in an arena from assembly code using NASM - the Netwide Assember and Perl:

Quarks replace unique strings with unique numbers and in doing so unite all that is best and brightest in dynamic trees, arrays, strings and short strings, all written in X86 assembler, all generated by Perl:

``` my $N = 5; my $a = CreateArena; # Arena containing quarks my $Q = $a->CreateQuarks; # Quarks

my $s = CreateShortString(0); # Short string used to load and unload quarks my $d = Rb(1..63);

for my $i(1..$N) # Load a set of quarks {my $j = $i - 1; $s->load(K(address, $d), K(size, 4+$i)); my $q = $Q->quarkFromShortString($s); $q->outNL("New quark $j: "); # New quark, new number } PrintOutNL;

for my $i(reverse 1..$N) # Reload a set of quarks {my $j = $i - 1; $s->load(K(address, $d), K(size, 4+$i)); my $q = $Q->quarkFromShortString($s); $q->outNL("Old quark $j: "); # Old quark, old number } PrintOutNL;

for my $i(1..$N) # Dump quarks {my $j = $i - 1; $s->clear; $Q->shortStringFromQuark(K(quark, $j), $s); PrintOutString "Quark string $j: "; PrintOutRegisterInHex xmm0; }

ok Assemble(debug => 0, trace => 0, eq => <

Old quark 4: 0000 0000 0000 0004 Old quark 3: 0000 0000 0000 0003 Old quark 2: 0000 0000 0000 0002 Old quark 1: 0000 0000 0000 0001 Old quark 0: 0000 0000 0000 0000

Quark string 0: xmm0: 0000 0000 0000 0000 0000 0504 0302 0105 Quark string 1: xmm0: 0000 0000 0000 0000 0006 0504 0302 0106 Quark string 2: xmm0: 0000 0000 0000 0000 0706 0504 0302 0107 Quark string 3: xmm0: 0000 0000 0000 0008 0706 0504 0302 0108 Quark string 4: xmm0: 0000 0000 0000 0908 0706 0504 0302 0109 END ```

Recursion with stack and parameter tracing from assembly code using NASM - the Netwide Assember and Perl:

Call a subroutine recursively and get a trace back showing the procedure calls and parameters passed to each call. Parameters are passed by reference not value.

``` my $d = V depth, 3; # Create a variable on the stack

my $s = Subroutine {my ($p, $s) = @_; # Parameters, subroutine descriptor PrintOutTraceBack;

my $d = $$p{depth}->copy($$p{depth} - 1);   # Modify the variable referenced by the parameter

If ($d > 0,
Then
 {$s->call($d);                             # Recurse
 });

PrintOutTraceBack;

} [qw(depth)], name => 'ref';

$s->call($d); # Call the subroutine

ok Assemble(debug => 0, eq => <

Subroutine trace back, depth: 0000 0000 0000 0001 0000 0000 0000 0003 ref

Subroutine trace back, depth: 0000 0000 0000 0002 0000 0000 0000 0002 ref 0000 0000 0000 0002 ref

Subroutine trace back, depth: 0000 0000 0000 0003 0000 0000 0000 0001 ref 0000 0000 0000 0001 ref 0000 0000 0000 0001 ref

Subroutine trace back, depth: 0000 0000 0000 0003 0000 0000 0000 0000 ref 0000 0000 0000 0000 ref 0000 0000 0000 0000 ref

Subroutine trace back, depth: 0000 0000 0000 0002 0000 0000 0000 0000 ref 0000 0000 0000 0000 ref

Subroutine trace back, depth: 0000 0000 0000 0001 0000 0000 0000 0000 ref

END ```

Process management from assembly code using NASM - the Netwide Assember and Perl:

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

``` use Nasm::X86 qw(:all);

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; # Assemble test and run

# 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 ```

For documentation see: CPAN