File Coverage

blib/lib/Nasm/X86.pm
Criterion Covered Total %
statement 99 966 10.2
branch 6 198 3.0
condition 0 8 0.0
subroutine 12 339 3.5
pod 59 306 19.2
total 176 1817 9.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/ -I. -I/home/phil/perl/cpan/AsmC/lib/
2             #-------------------------------------------------------------------------------
3             # Generate Nasm X86 code from Perl.
4             # Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             # Register expressions via op overloading - register size and ability to add offsets, peek, pop, push clear register
8             # Indent opcodes by call depth, - replace push @text with a method call
9             package Nasm::X86;
10             our $VERSION = "202104014";
11 1     1   939 use warnings FATAL => qw(all);
  1         8  
  1         39  
12 1     1   5 use strict;
  1         2  
  1         34  
13 1     1   6 use Carp qw(confess cluck);
  1         2  
  1         97  
14 1     1   644 use Data::Dump qw(dump);
  1         8604  
  1         65  
15 1     1   4192 use Data::Table::Text qw(:all);
  1         139951  
  1         1777  
16 1     1   765 use Asm::C qw(:all);
  1         3858  
  1         136  
17 1     1   7 use feature qw(say current_sub);
  1         3  
  1         1158  
18              
19             my $debug = -e q(/home/phil/); # Developing
20             my $sde = q(/var/isde/sde64); # Intel emulator
21             $sde = q(sde/sde64) unless $debug;
22              
23             binModeAllUtf8;
24              
25             my %rodata; # Read only data already written
26             my %rodatas; # Read only string already written
27             my %subroutines; # Subroutines generated
28             my @rodata; # Read only data
29             my @data; # Data
30             my @bss; # Block started by symbol
31             my @text; # Code
32              
33             my $sysout = 1; # File descriptor for output
34              
35             BEGIN{
36 1     1   6 my %r = ( map {$_=>'8'} qw(al bl cl dl r8b r9b r10b r11b r12b r13b r14b r15b sil dil spl bpl ah bh ch dh));
  20         43  
37 1         6 %r = (%r, map {$_=>'s'} qw(cs ds es fs gs ss));
  6         16  
38 1         6 %r = (%r, map {$_=>'16'} qw(ax bx cx dx r8w r9w r10w r11w r12w r13w r14w r15w si di sp bp));
  16         37  
39 1         8 %r = (%r, map {$_=>'32a'} qw(eax ebx ecx edx esi edi esp ebp));
  8         22  
40 1         8 %r = (%r, map {$_=>'32b'} qw(r8d r8l r9d r9l r10d r10l r11d r11l r12d r12l r13d r13l r14d r14l r15d r15l));
  16         52  
41 1         11 %r = (%r, map {$_=>'f'} qw(st0 st1 st2 st3 st4 st5 st6 st7));
  8         29  
42 1         10 %r = (%r, map {$_=>'64'} qw(rax rbx rcx rdx r8 r9 r10 r11 r12 r13 r14 r15 rsi rdi rsp rbp rip rflags));
  18         55  
43 1         15 %r = (%r, map {$_=>'64m'} qw(mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7));
  8         31  
44 1         13 %r = (%r, map {$_=>'128'} qw(xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31));
  32         85  
45 1         19 %r = (%r, map {$_=>'256'} qw(ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31));
  32         92  
46 1         22 %r = (%r, map {$_=>'512'} qw(zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31));
  32         88  
47 1         22 %r = (%r, map {$_=>'m'} qw(k0 k1 k2 k3 k4 k5 k6 k7));
  8         73  
48              
49 1         26 my @i0 = qw(pushfq rdtsc ret syscall); # Zero operand instructions
50 1         4 my @i1 = qw(call inc jge jmp jz pop push); # Single operand instructions
51 1         14 my @i2 = split /\s+/, <
52             add and cmp or lea mov shl shr sub test Vmovdqu8 vmovdqu32 vmovdqu64 vpxorq xor
53             END
54 1         4 my @i3 = split /\s+/, <
55             vprolq
56             END
57              
58 1         84 for my $r(sort keys %r) # Create register definitions
59 204     0 0 8389 {eval "sub $r\{q($r)\}";
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0            
60 204 50       832 confess $@ if $@;
61             }
62              
63 1         21 my %v = map {$_=>1} values %r;
  204         315  
64 1         17 for my $v(sort keys %v) # Types of register
65 12         1096 {my @r = grep {$r{$_} eq $v} sort keys %r;
  2448         3690  
66 12     0 0 128 eval "sub registers_$v\{".dump(\@r)."}";
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0            
67 12 50       15683 confess $@ if $@;
68             }
69              
70 1         4 if (1) # Instructions that take zero operands
71 1         3 {my $s = '';
72 1         3 for my $i(@i0)
73 4         8 {my $I = ucfirst $i;
74 4         12 $s .= <
75             sub $I()
76             {\@_ == 0 or confess "No arguments allowed";
77             push \@text, qq( $i\\n);
78             }
79             END
80             }
81 1 0   0 0 250 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0            
  0            
  0            
  0            
  0            
82 1 50       16 confess $@ if $@;
83             }
84              
85 1         2 if (1) # Instructions that take one operand
86 1         3 {my $s = '';
87 1         2 for my $i(@i1)
88 7         11 {my $I = ucfirst $i;
89 7         18 $s .= <
90             sub $I(\$)
91             {my (\$target) = \@_;
92             \@_ == 1 or confess "One argument required";
93             push \@text, qq( $i \$target\\n);
94             }
95             END
96             }
97 1 0   0 0 475 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 1    
  0 0   0 1    
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
98 1 50       8 confess $@ if $@;
99             }
100              
101 1         2 if (1) # Instructions that take two operands
102 1         3 {my $s = '';
103 1         3 for my $i(@i2)
104 15         26 {my $I = ucfirst $i;
105 15         29 $s .= <
106             sub $I(\$\$)
107             {my (\$target, \$source) = \@_;
108             \@_ == 2 or confess "Two arguments required";
109             push \@text, qq( $i \$target, \$source\\n);
110             }
111             END
112             }
113 1 0   0 0 1059 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
114 1 50       9 confess $@ if $@;
115             }
116              
117 1         2 if (1) # Instructions that take three operands
118 1         3 {my $s = '';
119 1         2 for my $i(@i3)
120 1         3 {my $I = ucfirst $i;
121 1         6 $s .= <
122             sub $I(\$\$\$)
123             {my (\$target, \$source, \$bits) = \@_;
124             \@_ == 3 or confess "Three arguments required";
125             push \@text, qq( $i \$target, \$source, \$bits\\n);
126             }
127             END
128             }
129 1 0   0 0 83 eval $s;
  0            
  0            
  0            
130 1 50       8354 confess $@ if $@;
131             }
132             }
133              
134             sub ClearRegisters(@); # Clear registers by setting them to zero
135             sub Comment(@); # Insert a comment into the assembly code
136             sub PeekR($); # Peek at the register on top of the stack
137             sub PopR(@); # Pop a list of registers off the stack
138             sub PrintOutMemory; # Print the memory addressed by rax for a length of rdi
139             sub PrintOutRegisterInHex($); # Print any register as a hex string
140             sub PushR(@);
141             sub Syscall(); # System call in linux 64 format per: https://filippo.io/linux-syscall-table/
142              
143             #D1 Data # Layout data
144              
145             my $Labels = 0;
146             sub Label #P Create a unique label
147 0     0 0   {"l".++$Labels; # Generate a label
148             }
149              
150             sub SetLabel($) # Set a label in the code section
151 0     0 1   {my ($l) = @_; # Label
152 0           push @text, <
153             $l:
154             END
155             }
156              
157             sub Ds(@) # Layout bytes in memory and return their label
158 0     0 1   {my (@d) = @_; # Data to be laid out
159 0           my $d = join '', @_;
160 0           $d =~ s(') (\')gs;
161 0           my $l = Label;
162 0           push @data, <
163             $l: db '$d';
164             END
165 0           $l # Return label
166             }
167              
168             sub Rs(@) # Layout bytes in read only memory and return their label
169 0     0 1   {my (@d) = @_; # Data to be laid out
170 0           my $d = join '', @_;
171 0           $d =~ s(') (\')gs;
172 0 0         return $_ if $_ = $rodatas{$d}; # Data already exists so return it
173 0           my $l = Label;
174 0           $rodatas{$d} = $l; # Record label
175 0           push @rodata, <
176             $l: db '$d',0;
177             END
178 0           $l # Return label
179             }
180              
181             sub Dbwdq($@) # Layout data
182 0     0 1   {my ($s, @d) = @_; # Element size, data to be laid out
183 0           my $d = join ', ', @d;
184 0           my $l = Label;
185 0           push @data, <
186             $l: d$s $d
187             END
188 0           $l # Return label
189             }
190              
191             sub Db(@) # Layout bytes in the data segment and return their label
192 0     0 1   {my (@bytes) = @_; # Bytes to layout
193 0           Dbwdq 'b', @_;
194             }
195             sub Dw(@) # Layout words in the data segment and return their label
196 0     0 1   {my (@words) = @_; # Words to layout
197 0           Dbwdq 'w', @_;
198             }
199             sub Dd(@) # Layout double words in the data segment and return their label
200 0     0 1   {my (@dwords) = @_; # Double words to layout
201 0           Dbwdq 'd', @_;
202             }
203             sub Dq(@) # Layout quad words in the data segment and return their label
204 0     0 1   {my (@qwords) = @_; # Quad words to layout
205 0           Dbwdq 'q', @_;
206             }
207              
208             sub Rbwdq($@) # Layout data
209 0     0 1   {my ($s, @d) = @_; # Element size, data to be laid out
210 0           my $d = join ', ', @d; # Data to be laid out
211 0 0         return $_ if $_ = $rodata{$d}; # Data already exists so return it
212 0           my $l = Label; # New data - create a label
213 0           push @rodata, <
214             $l: d$s $d
215             END
216 0           $rodata{$d} = $l; # Record label
217 0           $l # Return label
218             }
219              
220             sub Rb(@) # Layout bytes in the data segment and return their label
221 0     0 1   {my (@bytes) = @_; # Bytes to layout
222 0           Rbwdq 'b', @_;
223             }
224             sub Rw(@) # Layout words in the data segment and return their label
225 0     0 1   {my (@words) = @_; # Words to layout
226 0           Rbwdq 'w', @_;
227             }
228             sub Rd(@) # Layout double words in the data segment and return their label
229 0     0 1   {my (@dwords) = @_; # Double words to layout
230 0           Rbwdq 'd', @_;
231             }
232             sub Rq(@) # Layout quad words in the data segment and return their label
233 0     0 1   {my (@qwords) = @_; # Quad words to layout
234 0           Rbwdq 'q', @_;
235             }
236              
237             #D1 Registers # Operations on registers
238              
239             sub SaveFirstFour() # Save the first 4 parameter registers
240 0     0 1   {Push rax;
241 0           Push rdi;
242 0           Push rsi;
243 0           Push rdx;
244 0           4 * &RegisterSize(rax); # Space occupied by push
245             }
246              
247             sub RestoreFirstFour() # Restore the first 4 parameter registers
248 0     0 1   {Pop rdx;
249 0           Pop rsi;
250 0           Pop rdi;
251 0           Pop rax;
252             }
253              
254             sub RestoreFirstFourExceptRax() # Restore the first 4 parameter registers except rax so it can return its value
255 0     0 1   {Pop rdx;
256 0           Pop rsi;
257 0           Pop rdi;
258 0           Add rsp, 8;
259             }
260              
261             sub SaveFirstSeven() # Save the first 7 parameter registers
262 0     0 1   {Push rax;
263 0           Push rdi;
264 0           Push rsi;
265 0           Push rdx;
266 0           Push r10;
267 0           Push r8;
268 0           Push r9;
269 0           7 * RegisterSize(rax); # Space occupied by push
270             }
271              
272             sub RestoreFirstSeven() # Restore the first 7 parameter registers
273 0     0 1   {Pop r9;
274 0           Pop r8;
275 0           Pop r10;
276 0           Pop rdx;
277 0           Pop rsi;
278 0           Pop rdi;
279 0           Pop rax;
280             }
281              
282             sub RestoreFirstSevenExceptRax() # Restore the first 7 parameter registers except rax which is being used to return the result
283 0     0 1   {Pop r9;
284 0           Pop r8;
285 0           Pop r10;
286 0           Pop rdx;
287 0           Pop rsi;
288 0           Pop rdi;
289 0           Add rsp, RegisterSize(rax); # Skip rax
290             }
291              
292             sub RestoreFirstSevenExceptRaxAndRdi() # Restore the first 7 parameter registers except rax and rdi which are being used to return the results
293 0     0 1   {Pop r9;
294 0           Pop r8;
295 0           Pop r10;
296 0           Pop rdx;
297 0           Pop rsi;
298 0           Add rsp, 2*RegisterSize(rax); # Skip rdi and rax
299             }
300              
301             sub RegisterSize($) # Return the size of a register
302 0     0 1   {my ($r) = @_; # Register
303 0 0         return 16 if $r =~ m(\Ax);
304 0 0         return 32 if $r =~ m(\Ay);
305 0 0         return 64 if $r =~ m(\Az);
306 0           8
307             }
308              
309             sub ClearRegisters(@) # Clear registers by setting them to zero
310 0     0 1   {my (@registers) = @_; # Registers
311 0 0         @_ == 1 or confess;
312 0           for my $r(@registers)
313 0           {my $size = RegisterSize $r;
314 0 0         Xor $r, $r if $size == 8;
315 0 0         Vpxorq $r, $r if $size > 8;
316             }
317             }
318              
319             #D1 Structured Programming # Structured programming constructs
320              
321             sub If(&;&) # If
322 0     0 1   {my ($then, $else) = @_; # Then - required , else - optional
323 0 0         @_ >= 1 or confess;
324 0 0         if (@_ == 1) # No else
325 0           {Comment "if then";
326 0           my $end = Label;
327 0           Jz $end;
328 0           &$then;
329 0           SetLabel $end;
330             }
331             else # With else
332 0           {Comment "if then else";
333 0           my $endIf = Label;
334 0           my $startElse = Label;
335 0           Jz $startElse;
336 0           &$then;
337 0           Jmp $endIf;
338 0           SetLabel $startElse;
339 0           &$else;
340 0           SetLabel $endIf;
341             }
342             }
343              
344             sub For(&$$$) # For
345 0     0 1   {my ($body, $register, $limit, $increment) = @_; # Body, register, limit on loop, increment
346 0 0         @_ == 4 or confess;
347 0           Comment "For $register $limit";
348 0           my $start = Label;
349 0           my $end = Label;
350 0           SetLabel $start;
351 0           Cmp $register, $limit;
352 0           Jge $end;
353              
354 0           &$body;
355              
356 0 0         if ($increment == 1)
357 0           {Inc $register;
358             }
359             else
360 0           {Add $register, $increment;
361             }
362 0           Jmp $start;
363 0           SetLabel $end;
364             }
365              
366             sub S(&%) # 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
367 0     0 1   {my ($body, %options) = @_; # Body, options.
368 0 0         @_ >= 1 or confess;
369 0           my $name = $options{name}; # Optional name for subroutine reuse
370 0           my $comment = $options{comment}; # Optional comment
371 0   0       Comment "Subroutine " .($comment//'');
372              
373 0 0 0       if ($name and my $n = $subroutines{$name}) {return $n} # Return the label of a pre-existing copy of the code
  0            
374              
375 0           my $start = Label;
376 0           my $end = Label;
377 0           Jmp $end;
378 0           SetLabel $start;
379 0           &$body;
380 0           Ret;
381 0           SetLabel $end;
382 0 0         $subroutines{$name} = $start if $name; # Cache a reference to the generated code if a name was supplied
383              
384 0           $start
385             }
386              
387             sub Comment(@) # Insert a comment into the assembly code
388 0     0 1   {my (@comment) = @_; # Text of comment
389 0           my $c = join "", @comment;
390 0           push @text, <
391             ; $c
392             END
393             }
394              
395             #D1 Print # Print
396              
397             sub PrintOutNl() # Write a new line
398 0 0   0 1   {@_ == 0 or confess;
399 0           my $a = Rb(10);
400 0           Comment "Write new line";
401 0           SaveFirstFour;
402 0           Mov rax, 1;
403 0           Mov rdi, 1;
404 0           Mov rsi, $a;
405 0           Mov rdx, 1;
406 0           Syscall;
407 0           RestoreFirstFour()
408             }
409              
410             sub PrintOutString($) # Write a constant string to sysout.
411 0     0 1   {my ($string) = @_; # String
412 0 0         @_ == 1 or confess;
413              
414 0           SaveFirstFour;
415 0           Comment "Write String: $string";
416 0           my ($c) = @_;
417 0           my $l = length($c);
418 0           my $a = Rs($c);
419 0           Mov rax, 1;
420 0           Mov rdi, $sysout;
421 0           Mov rsi, $a;
422 0           Mov rdx, $l;
423 0           Syscall;
424 0           RestoreFirstFour();
425             }
426              
427             sub PrintOutRaxInHex # Write the content of register rax to stderr in hexadecimal in big endian notation
428 0 0   0 1   {@_ == 0 or confess;
429 0           Comment "Print Rax In Hex";
430              
431             my $hexTranslateTable = sub
432 0     0     {my $h = '0123456789ABCDEF';
433 0           my @t;
434 0           for my $i(split //, $h)
435 0           {for my $j(split //, $h)
436 0           {push @t, "$i$j";
437             }
438             }
439             Rs @t # Constant strings are only saved if they are unique, else a read only copy is returned.
440 0           }->();
  0            
441              
442             my $sub = S # Address conversion routine
443 0     0     {SaveFirstFour;
444 0           Mov rdx, rax; # Content to be printed
445 0           Mov rdi, 2; # Length of a byte in hex
446 0           for my $i(0..7)
447 0           {my $s = 8*$i;
448 0           Mov rax,rdx;
449 0           Shl rax,$s; # Push selected byte high
450 0           Shr rax,56; # Push select byte low
451 0           Shl rax,1; # Multiply by two because each entry in the translation table is two bytes long
452 0           Lea rax, "[$hexTranslateTable+rax]";
453 0           PrintOutMemory;
454 0 0         PrintOutString ' ' if $i % 2;
455             }
456 0           RestoreFirstFour;
457 0           } name => "PrintOutRaxInHex";
458              
459 0           Call $sub;
460             }
461              
462             sub ReverseBytesInRax # Reverse the bytes in rax
463 0 0   0 1   {@_ == 0 or confess;
464 0           Comment "Reverse bytes in rax";
465              
466             my $sub = S # Reverse rax
467 0     0     {my $size = RegisterSize rax;
468 0           SaveFirstFour;
469 0           ClearRegisters rsi;
470 0           for(1..$size) # Reverse each byte
471 0           {Mov rdi,rax;
472 0           Shr rdi,($_-1)*8;
473 0           Shl rdi,($size-1)*8;
474 0           Shr rdi,($_-1)*8;
475 0           Or rsi,rdi;
476             }
477 0           Mov rax,rsi;
478 0           RestoreFirstFourExceptRax;
479 0           } name => "ReverseBytesInRax";
480              
481 0           Call $sub;
482             }
483              
484             sub PrintOutRaxInReverseInHex # Write the content of register rax to stderr in hexadecimal in little endian notation
485 0 0   0 1   {@_ == 0 or confess;
486 0           Comment "Print Rax In Reverse In Hex";
487 0           ReverseBytesInRax;
488 0           PrintOutRaxInHex;
489             }
490              
491             sub PrintOutRegisterInHex($) # Print any register as a hex string
492 0     0 1   {my ($r) = @_; # Name of the register to print
493 0           Comment "Print register $r in Hex";
494 0 0         @_ == 1 or confess;
495              
496             my $sub = S # Reverse rax
497 0           {PrintOutString sprintf("%6s: ", $r);
498              
499             my sub printReg(@) # Print the contents of a register
500 0           {my (@regs) = @_; # Size in bytes, work registers
501 0           my $s = RegisterSize $r; # Size of the register
502 0           PushR @regs; # Save work registers
503 0           PushR $r; # Place register contents on stack
504 0           PopR @regs; # Load work registers
505 0           for my $R(@regs) # Print work registers to print input register
506 0 0         {if ($R !~ m(\Arax))
507 0           {PrintOutString(" ");
508 0           Mov rax, $R
509             }
510 0           PrintOutRaxInHex; # Print work register
511             }
512 0           PopR @regs;
513             };
514 0 0         if ($r =~ m(\Ar)) {printReg qw(rax)} # 64 bit register requested
  0 0          
    0          
    0          
515 0           elsif ($r =~ m(\Ax)) {printReg qw(rax rbx)} # xmm*
516 0           elsif ($r =~ m(\Ay)) {printReg qw(rax rbx rcx rdx)} # ymm*
517 0           elsif ($r =~ m(\Az)) {printReg qw(rax rbx rcx rdx r8 r9 r10 r11)} # zmm*
518              
519 0           PrintOutNl;
520 0           } name => "PrintOutRegister${r}InHex"; # One routine per register printed
521              
522 0           Call $sub;
523             }
524              
525             sub PrintOutRipInHex # Print the instruction pointer in hex
526 0 0   0 1   {@_ == 0 or confess;
527 0           my @regs = qw(rax);
528             my $sub = S
529 0     0     {PushR @regs;
530 0           my $l = Label;
531 0           push @text, <
532             $l:
533             END
534 0           Lea rax, "[$l]"; # Current instruction pointer
535 0           PrintOutString "rip: ";
536 0           PrintOutRaxInHex;
537 0           PrintOutNl;
538 0           PopR @regs;
539 0           } name=> "PrintOutRipInHex";
540              
541 0           Call $sub;
542             }
543              
544             sub PrintOutRflagsInHex # Print the flags register in hex
545 0 0   0 1   {@_ == 0 or confess;
546 0           my @regs = qw(rax);
547              
548             my $sub = S
549 0     0     {PushR @regs;
550 0           Pushfq;
551 0           Pop rax;
552 0           PrintOutString "rfl: ";
553 0           PrintOutRaxInHex;
554 0           PrintOutNl;
555 0           PopR @regs;
556 0           } name=> "PrintOutRflagsInHex";
557              
558 0           Call $sub;
559             }
560              
561             sub PrintOutRegistersInHex # Print the general purpose registers in hex
562 0 0   0 1   {@_ == 0 or confess;
563              
564             my $sub = S
565 0     0     {PrintOutRipInHex;
566 0           PrintOutRflagsInHex;
567              
568 0           my @regs = qw(rax);
569 0           PushR @regs;
570              
571 0           my $w = registers_64();
572 0           for my $r(sort @$w)
573 0 0         {next if $r =~ m(rip|rflags);
574 0 0         if ($r eq rax)
575 0           {Pop rax;
576 0           Push rax
577             }
578 0           PrintOutString reverse(pad(reverse($r), 3)).": ";
579 0           Mov rax, $r;
580 0           PrintOutRaxInHex;
581 0           PrintOutNl;
582             }
583 0           PopR @regs;
584 0           } name=> "PrintOutRegistersInHex";
585              
586 0           Call $sub;
587             }
588              
589             #D1 Processes # Create and manage processes
590              
591             sub Fork() # Fork
592 0 0   0 1   {@_ == 0 or confess;
593 0           Comment "Fork";
594 0           Mov rax, 57;
595 0           Syscall
596             }
597              
598             sub GetPid() # Get process identifier
599 0 0   0 1   {@_ == 0 or confess;
600 0           Comment "Get Pid";
601              
602 0           Mov rax, 39;
603 0           Syscall
604             }
605              
606             sub GetPPid() # Get parent process identifier
607 0 0   0 1   {@_ == 0 or confess;
608 0           Comment "Get Parent Pid";
609              
610 0           Mov rax, 110;
611 0           Syscall
612             }
613              
614             sub GetUid() # Get userid of current process
615 0 0   0 1   {@_ == 0 or confess;
616 0           Comment "Get User id";
617              
618 0           Mov rax, 102;
619 0           Syscall
620             }
621              
622             sub WaitPid() # Wait for the pid in rax to complete
623 0 0   0 1   {@_ == 0 or confess;
624 0           Comment "WaitPid - wait for the pid in rax";
625 0           SaveFirstSeven;
626 0           Mov rdi,rax;
627 0           Mov rax, 61;
628 0           Mov rsi, 0;
629 0           Mov rdx, 0;
630 0           Mov r10, 0;
631 0           Syscall;
632 0           RestoreFirstSevenExceptRax;
633             }
634              
635             sub ReadTimeStampCounter() # Read the time stamp counter and return the time in nanoseconds in rax
636 0 0   0 1   {@_ == 0 or confess;
637 0           Comment "Read Time-Stamp Counter";
638 0           Push rdx;
639 0           Rdtsc;
640 0           Shl rdx,32; # Or upper half into rax
641 0           Or rax,rdx;
642 0           Pop rdx;
643 0           RestoreFirstFourExceptRax;
644             }
645              
646             #D1 Stack # Manage data on the stack
647              
648             #D2 Push, Pop, Peek # Generic versions of push, pop, peek
649              
650             sub PushR(@) # Push registers onto the stack
651 0     0 1   {my (@r) = @_; # Register
652 0           for my $r(@r)
653 0           {my $size = RegisterSize $r;
654 0 0         if ($size > 8)
655 0           {Sub rsp, $size;
656 0           Vmovdqu32 "[rsp]", $r;
657             }
658             else
659 0           {Push $r;
660             }
661             }
662             }
663              
664             sub PopR(@) # Pop registers from the stack
665 0     0 1   {my (@r) = @_; # Register
666 0           for my $r(reverse @r) # Pop registers in reverse order
667 0           {my $size = RegisterSize $r;
668 0 0         if ($size > 8)
669 0           {Vmovdqu32 $r, "[rsp]";
670 0           Add(rsp, $size);
671             }
672             else
673 0           {Pop $r;
674             }
675             }
676             }
677              
678             sub PeekR($) # Peek at register on stack
679 0     0 1   {my ($r) = @_; # Register
680 0           my $size = RegisterSize $r;
681 0 0         if ($size > 8) # x|y|zmm*
682 0           {Vmovdqu32 $r, "[rsp]";
683             }
684             else # 8 byte register
685 0           {Mov $r, "[rsp]";
686             }
687             }
688              
689             #D2 Declarations # Declare variables and structures
690              
691             #D3 Structures # Declare a structure
692              
693             sub Structure($) # Create a structure addressed by a register
694 0     0 0   {my ($register) = @_; # Register locating the structure
695 0 0         @_ == 1 or confess;
696 0           my $local = genHash("Structure",
697             base => $register,
698             size => 0,
699             variables => [],
700             );
701             }
702              
703             sub Structure::field($$;$) # Add a field of the specified length with an optional comment
704 0     0     {my ($structure, $length, $comment) = @_; # Structure data descriptor, length of data, optional comment
705 0 0         @_ >= 2 or confess;
706 0           my $variable = genHash("StructureField",
707             structure => $structure,
708             loc => $structure->size,
709             size => $length,
710             comment => $comment
711             );
712 0           $structure->size += $length; # Update size of local data
713 0           $variable
714             }
715              
716             sub StructureField::addr($) # Address a field in a structure
717 0     0     {my ($field) = @_; # Field
718 0 0         @_ == 1 or confess;
719 0           my $loc = $field->loc; # Offset of field in structure
720 0           my $reg = $field->structure->base; # Register locating the structure
721 0           "[$loc+$reg]" # Address field
722             }
723              
724             sub All8Structure($$) # Create a structure consisting of 8 byte fields
725 0     0 0   {my ($base, $N) = @_; # Base register, Number of variables required
726 0 0         @_ == 2 or confess;
727 0           my $s = Structure $base; # Structure of specified size based on specified register
728 0           my @f;
729 0           for(1..$N) # Create the variables
730 0           {push @f, $s->field(RegisterSize rax)->addr;
731             }
732 0           ($s, @f) # Structure, fields
733             }
734              
735             #D3 Stack Frame # Declare local variables in a frame on the stack
736              
737             sub LocalData() # Map local data
738 0 0   0 1   {@_ == 0 or confess;
739 0           my $local = genHash("LocalData",
740             size => 0,
741             variables => [],
742             );
743             }
744              
745             sub LocalData::start($) # Start a local data area on the stack
746 0     0     {my ($local) = @_; # Local data descriptor
747 0 0         @_ == 1 or confess;
748 0           my $size = $local->size; # Size of local data
749 0           Push rbp;
750 0           Mov rbp,rsp;
751 0           Sub rsp, $size;
752             }
753              
754             sub LocalData::free($) # Free a local data area on the stack
755 0     0     {my ($local) = @_; # Local data descriptor
756 0 0         @_ == 1 or confess;
757 0           Mov rsp,rbp;
758 0           Pop rbp;
759             }
760              
761             sub LocalData::variable($$;$) # Add a local variable
762 0     0     {my ($local, $length, $comment) = @_; # Local data descriptor, length of data, optional comment
763 0 0         @_ >= 2 or confess;
764 0           my $variable = genHash("LocalVariable",
765             loc => $local->size,
766             size => $length,
767             comment => $comment
768             );
769 0           $local->size += $length; # Update size of local data
770 0           $variable
771             }
772              
773             sub LocalVariable::stack($) # Address a local variable on the stack
774 0     0     {my ($variable) = @_; # Variable
775 0 0         @_ == 1 or confess;
776 0           my $loc = $variable->loc; # Location of variable on stack
777 0           "[$loc+rbp]" # Address variable
778             }
779              
780             sub LocalData::allocate8($@) # Add some 8 byte local variables and return an array of variable definitions
781 0     0     {my ($local, @comments) = @_; # Local data descriptor, optional comment
782 0           my @v;
783 0           for my $c(@comments)
784 0           {push @v, LocalData::variable($local, 8, $c);
785             }
786 0 0         wantarray ? @v : $v[-1]; # Avoid returning the number of elements accidently
787             }
788              
789             sub 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...)
790 0     0 1   {my ($N) = @_; # Number of variables required
791 0           my $local = LocalData; # Create local data descriptor
792 0           my @v;
793 0           for(1..$N) # Create the variables
794 0           {my $v = $local->variable(RegisterSize(rax));
795 0           push @v, $v->stack;
796             }
797 0           $local->start; # Create the local data area on the stack
798 0           ($local, @v)
799             }
800              
801             #D1 Memory # Allocate and print memory
802              
803             sub PrintOutMemoryInHex # Dump memory from the address in rax for the length in rdi
804 0 0   0 1   {@_ == 0 or confess;
805 0           Comment "Print out memory in hex";
806              
807             my $sub = S
808 0     0     {my $size = RegisterSize rax;
809 0           SaveFirstFour;
810 0           Mov rsi,rax; # Position in memory
811 0           Lea rdi,"[rax+rdi-$size+1]"; # Upper limit of printing with an 8 byte register
812             For # Print string in blocks
813 0           {Mov rax, "[rsi]";
814 0           ReverseBytesInRax;
815 0           PrintOutRaxInHex;
816 0           } rsi, rdi, $size;
817 0           RestoreFirstFour;
818 0           } name=> "PrintOutMemoryInHex";
819              
820 0           Call $sub;
821             }
822              
823             sub PrintOutMemory # Print the memory addressed by rax for a length of rdi
824 0 0   0 1   {@_ == 0 or confess;
825 0           Comment "Print memory";
826 0           SaveFirstFour;
827 0           Mov rsi, rax;
828 0           Mov rdx, rdi;
829 0           Mov rax, 1;
830 0           Mov rdi, $sysout;
831 0           Syscall;
832 0           RestoreFirstFour();
833             }
834              
835             sub AllocateMemory # Allocate the amount of memory specified in rax via mmap and return the address of the allocated memory in rax
836 0 0   0 1   {@_ == 0 or confess;
837 0           Comment "Allocate memory";
838              
839             my $sub = S
840 0     0     {SaveFirstSeven;
841 0           my $d = extractMacroDefinitionsFromCHeaderFile "linux/mman.h"; # mmap constants
842 0           my $pa = $$d{MAP_PRIVATE} | $$d{MAP_ANONYMOUS};
843 0           my $wr = $$d{PROT_WRITE} | $$d{PROT_READ};
844              
845 0           Mov rsi, rax; # Amount of memory
846 0           Mov rax, 9; # mmap
847 0           Xor rdi, rdi; # Anywhere
848 0           Mov rdx, $wr; # Read write protections
849 0           Mov r10, $pa; # Private and anonymous map
850 0           Mov r8, -1; # File descriptor for file backing memory if any
851 0           Mov r9, 0; # Offset into file
852 0           Syscall;
853 0           RestoreFirstSevenExceptRax;
854 0           } name=> "AllocateMemory";
855              
856 0           Call $sub;
857             }
858              
859             sub FreeMemory # Free memory via mmap. The address of the memory is in rax, the length to free is in rdi
860 0 0   0 1   {@_ == 0 or confess;
861 0           Comment "Free memory";
862             my $sub = S
863 0     0     {SaveFirstFour;
864 0           Mov rsi, rdi;
865 0           Mov rdi, rax;
866 0           Mov rax, 11;
867 0           Syscall;
868 0           RestoreFirstFourExceptRax;
869 0           } name=> "FreeMemory";
870              
871 0           Call $sub;
872             }
873              
874             sub MemoryClear() # Clear memory - the address of the memory is in rax, the length in rdi
875 0 0   0 1   {@_ == 0 or confess;
876 0           Comment "Clear memory";
877              
878 0           my $size = RegisterSize zmm0;
879 0           my $saveSize = SaveFirstFour; # Generated code
880 0           PushR zmm0; # Pump zeros with this register
881 0           Lea rdi, "[rax+rdi-$size]"; # Address of upper limit of buffer
882 0           ClearRegisters zmm0; # Clear the register that will be written into memory
883              
884             For # Clear memory
885 0     0     {Vmovdqu64 "[rax]", zmm0;
886 0           } rax, rdi, RegisterSize zmm0;
887              
888 0           PopR zmm0;
889 0           RestoreFirstFour;
890             }
891              
892             #D1 Files # Process a file
893              
894             sub OpenRead() # Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
895 0 0   0 1   {@_ == 0 or confess;
896 0           Comment "Open a file for read";
897              
898             my $sub = S
899 0     0     {my $S = extractMacroDefinitionsFromCHeaderFile "asm-generic/fcntl.h"; # Constants for reading a file
900 0           my $O_RDONLY = $$S{O_RDONLY};
901 0           SaveFirstFour;
902 0           Mov rdi,rax;
903 0           Mov rax,2;
904 0           Mov rsi,$O_RDONLY;
905 0           Xor rdx,rdx;
906 0           Syscall;
907 0           RestoreFirstFourExceptRax;
908 0           } name=> "OpenRead";
909              
910 0           Call $sub;
911             }
912              
913             sub Close($) # Close a file descriptor
914 0     0 1   {my ($fdes) = @_; # File descriptor
915 0 0         @_ == 1 or confess;
916 0           Comment "Close a file";
917 0           SaveFirstFour;
918 0           Mov rdi,$fdes;
919 0           Mov rax,3;
920 0           Syscall;
921 0           RestoreFirstFourExceptRax;
922             }
923              
924             sub StatSize() # Stat a file whose name is addressed by rax to get its size in rax
925 0 0   0 1   {@_ == 0 or confess;
926 0           Comment "Stat a file for size";
927 0           my $S = extractCStructure "#include "; # Get location of size field
928 0           my $Size = $$S{stat}{size};
929 0           my $off = $$S{stat}{fields}{st_size}{loc};
930              
931 0           SaveFirstFour;
932 0           Mov rdi, rax; # File name
933 0           Mov rax,4;
934 0           Lea rsi, "[rsp-$Size]";
935 0           Syscall;
936 0           Mov rax, "[$off+rsp-$Size]"; # Place size in rax
937 0           RestoreFirstFourExceptRax;
938             }
939              
940             sub 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
941 0 0   0 1   {@_ == 0 or confess;
942 0           Comment "Read a file into memory";
943              
944 0           SaveFirstSeven; # Generated code
945 0           my ($local, $file, $addr, $size, $fdes) = AllocateAll8OnStack 4; # Local data
946              
947 0           Mov $file, rax; # Save file name
948              
949 0           StatSize; # File size
950 0           Mov $size, rax; # Save file size
951              
952 0           Mov rax, $file; # File name
953 0           OpenRead; # Open file for read
954 0           Mov $fdes, rax; # Save file descriptor
955              
956 0           my $d = extractMacroDefinitionsFromCHeaderFile "linux/mman.h"; # mmap constants
957 0           my $pa = $$d{MAP_PRIVATE};
958 0           my $ro = $$d{PROT_READ};
959              
960 0           Mov rax, 9; # mmap
961 0           Mov rsi, $size; # Amount of memory
962 0           Xor rdi, rdi; # Anywhere
963 0           Mov rdx, $ro; # Read write protections
964 0           Mov r10, $pa; # Private and anonymous map
965 0           Mov r8, $fdes; # File descriptor for file backing memory
966 0           Mov r9, 0; # Offset into file
967 0           Syscall;
968 0           Mov rdi, $size;
969 0           RestoreFirstSevenExceptRaxAndRdi;
970             }
971              
972             #D1 Strings # Operations on Strings
973              
974             sub CreateByteString() # Create an relocatable string of bytes in an arena and returns its address in rax
975 0 0   0 0   {@_ == 0 or confess;
976 0           Comment "Create byte string";
977 0           my $N = 4096; # Initial size of string
978              
979 0           my ($string, $size, $used, $data) = All8Structure rax, 3; # String base
980              
981             my $sub = S # Create string
982 0     0     {SaveFirstFour;
983 0           Mov rax, $N;
984 0           AllocateMemory;
985 0           ClearRegisters rdi;
986 0           Mov $used, rdi;
987 0           Mov rdi, $N;
988 0           Mov $size, rdi;
989              
990 0           RestoreFirstFourExceptRax;
991 0           } name=> "CreateByteString";
992              
993 0           Call $sub;
994              
995 0           genHash("ByteString", # Definition of byte string
996             structure => $string, # Structure details
997             size => $size, # Size field details
998             used => $used, # Used field details
999             data => $data, # The first 8 bytes of the data
1000             );
1001             }
1002              
1003             sub ByteString::ar($) # Append the content of rdi to the byte string addressed by rax
1004 0     0     {my ($byteString) = @_; # Byte string descriptor
1005 0           my $size = $byteString->size;
1006 0           my $used = $byteString->used;
1007 0           my $data = $byteString->data;
1008              
1009 0           SaveFirstFour;
1010 0           Lea rsi, $data; # Address of data field
1011 0           Add rsi, $used; # Skip over used data
1012 0           Mov "[rsi]", rdi; # Move data in
1013              
1014 0           Mov rsi, $used; # Increment used
1015 0           Add rsi, RegisterSize rdi;
1016 0           Mov $used, rsi;
1017 0           RestoreFirstFour;
1018             }
1019              
1020             sub ByteString::out($) # Print the specified byte string addressed by rax on sysout
1021 0     0     {my ($byteString) = @_; # Byte string descriptor
1022 0           my $used = $byteString->used;
1023 0           my $data = $byteString->data;
1024 0           SaveFirstFour;
1025 0           Mov rdi, $used; # Length to print
1026 0           Lea rax, $data; # Address of data field
1027 0           PrintOutMemory;
1028 0           RestoreFirstFour;
1029             }
1030              
1031             #D1 Assemble # Assemble generated code
1032              
1033             sub Start() # Initialize the assembler
1034 0     0 1   {@bss = @data = @rodata = %rodata = %rodatas = %subroutines = @text = ();
1035 0           $Labels = 0;
1036             }
1037              
1038             sub Exit(;$) # Exit with the specified return code or zero if no return code supplied
1039 0     0 1   {my ($c) = @_; # Return code
1040 0 0 0       if (@_ == 0 or $c == 0)
    0          
1041 0           {Comment "Exit code: 0";
1042 0           ClearRegisters rdi;
1043             }
1044             elsif (@_ == 1)
1045 0           {Comment "Exit code: $c";
1046 0           Mov rdi, $c;
1047             }
1048 0           Mov rax, 60;
1049 0           Syscall;
1050             }
1051              
1052             sub Assemble(%) # Assemble the generated code
1053 0     0 0   {my (%options) = @_; # Options
1054 0           my $r = join "\n", map {s/\s+\Z//sr} @rodata;
  0            
1055 0           my $d = join "\n", map {s/\s+\Z//sr} @data;
  0            
1056 0           my $b = join "\n", map {s/\s+\Z//sr} @bss;
  0            
1057 0           my $t = join "\n", map {s/\s+\Z//sr} @text;
  0            
1058 0           my $a = <
1059             section .rodata
1060             $r
1061             section .data
1062             $d
1063             section .bss
1064             $b
1065             section .text
1066             global _start, main
1067             _start:
1068             main:
1069             push rbp ; function prologue
1070             mov rbp,rsp
1071             $t
1072             END
1073              
1074 0           my $c = owf(q(z.asm), $a); # Source file
1075 0           my $e = q(z); # Executable file
1076 0           my $l = q(z.txt); # Assembler listing
1077 0           my $o = q(z.o); # Object file
1078              
1079 0           my $cmd = qq(nasm -f elf64 -g -l $l -o $o $c; ld -o $e $o; chmod 744 $e; $sde -ptr-check -- ./$e 2>&1);
1080 0           say STDERR qq($cmd);
1081 0           my $R = eval {qx($cmd)};
  0            
1082 0           say STDERR $R;
1083 0           unlink $e, $o; # Delete object and executable leaving listing files
1084 0           $R # Return execution results
1085             }
1086              
1087             #d
1088             #-------------------------------------------------------------------------------
1089             # Export - eeee
1090             #-------------------------------------------------------------------------------
1091              
1092 1     1   13 use Exporter qw(import);
  1         2  
  1         37  
1093              
1094 1     1   6 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         466  
1095              
1096             @ISA = qw(Exporter);
1097             @EXPORT = qw();
1098             @EXPORT_OK = qw(
1099             );
1100             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
1101              
1102             # podDocumentation
1103             =pod
1104              
1105             =encoding utf-8
1106              
1107             =head1 Name
1108              
1109             Nasm::X86 - Generate Nasm assembler code
1110              
1111             =head1 Synopsis
1112              
1113             Write and execute x64 instructions from perl, using perl as a macro assembler
1114             as shown in the following examples.
1115              
1116             =head2 Avx512 instructions
1117              
1118             Use avx512 instructions to reorder data using 512 bit zmm registers:
1119              
1120             Start;
1121             my $q = Rs my $s = join '', ('a'..'p')x4;;
1122             Mov rax, Ds('0'x128);
1123              
1124             Vmovdqu32 zmm0, "[$q]";
1125             Vprolq zmm1, zmm0, 32;
1126             Vmovdqu32 "[rax]", zmm1;
1127              
1128             Mov rdi, length $s;
1129             PrintOutMemory;
1130             Exit;
1131              
1132             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
1133             ok Assemble =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
1134              
1135             =head2 Dynamic string held in an arena
1136              
1137             Create a dynamic byte string, add some content to it and then print it.
1138              
1139             Start; # Start the program
1140             my $s = CreateByteString; # Create a string
1141             Mov rdi, 0x68676665; # Load a string to append
1142             Shl rdi, 32;
1143             Or rdi, 0x64636261;
1144             $s->ar; # Add a string held in a register
1145             $s->ar;
1146             $s->out; # Print byte string
1147             Exit; # Return to operating system
1148              
1149             Assemble =~ m(abcdefghabcdefgh); # Assemble and execute
1150              
1151              
1152             =head2 Process management
1153              
1154             Start a child process and wait for it, printing out the process identifiers of
1155             each process involved:
1156              
1157             Start; # Start the program
1158             Fork; # Fork
1159              
1160             Test rax,rax;
1161             If # Parent
1162             {Mov rbx, rax;
1163             WaitPid;
1164             PrintOutRegisterInHex rax;
1165             PrintOutRegisterInHex rbx;
1166             GetPid; # Pid of parent as seen in parent
1167             Mov rcx,rax;
1168             PrintOutRegisterInHex rcx;
1169             }
1170             sub # Child
1171             {Mov r8,rax;
1172             PrintOutRegisterInHex r8;
1173             GetPid; # Child pid as seen in child
1174             Mov r9,rax;
1175             PrintOutRegisterInHex r9;
1176             GetPPid; # Parent pid as seen in child
1177             Mov r10,rax;
1178             PrintOutRegisterInHex r10;
1179             };
1180              
1181             Exit; # Return to operating system
1182              
1183             my $r = Assemble;
1184              
1185             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1186             # r9: 0000 0000 0003 0C63 #2 Pid of child
1187             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1188             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1189             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1190             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1191              
1192             =head2 Read a file
1193              
1194             Read this file:
1195              
1196             Start; # Start the program
1197             Mov rax, Rs($0); # File to read
1198             ReadFile; # Read file
1199             PrintOutMemory; # Print memory
1200             Exit; # Return to operating system
1201              
1202             my $r = Assemble; # Assemble and execute
1203             ok index($r, readFile($0)) > -1; # Output contains this file
1204              
1205             =head2 Installation
1206              
1207             You will need the Intel Software Development Emulator and the Networkwide
1208             Assembler installed on your test system. For full details of how to do this
1209             see: L
1210              
1211             =head1 Description
1212              
1213             Generate Nasm assembler code
1214              
1215              
1216             Version "202104013".
1217              
1218              
1219             The following sections describe the methods in each functional area of this
1220             module. For an alphabetic listing of all methods by name see L.
1221              
1222              
1223              
1224             =head1 Data
1225              
1226             Layou data
1227              
1228             =head2 SetLabel($l)
1229              
1230             Set a label in the code section
1231              
1232             Parameter Description
1233             1 $l Label
1234              
1235             =head2 Ds(@d)
1236              
1237             Layout bytes in memory and return their label
1238              
1239             Parameter Description
1240             1 @d Data to be laid out
1241              
1242             B
1243              
1244              
1245             Start;
1246             my $q = Rs('a'..'z');
1247              
1248             Mov rax, Ds('0'x64); # Output area # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1249              
1250             Vmovdqu32(xmm0, "[$q]"); # Load
1251             Vprolq (xmm0, xmm0, 32); # Rotate double words in quad words
1252             Vmovdqu32("[rax]", xmm0); # Save
1253             Mov rdi, 16;
1254             PrintOutMemory;
1255             Exit;
1256             ok assemble() =~ m(efghabcdmnopijkl)s;
1257              
1258              
1259             =head2 Rs(@d)
1260              
1261             Layout bytes in read only memory and return their label
1262              
1263             Parameter Description
1264             1 @d Data to be laid out
1265              
1266             B
1267              
1268              
1269             Start;
1270             Comment "Print a string from memory";
1271             my $s = "Hello World";
1272              
1273             Mov rax, Rs($s); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1274              
1275             Mov rdi, length $s;
1276             PrintOutMemory;
1277             Exit;
1278             ok assemble =~ m(Hello World);
1279              
1280              
1281             =head2 Dbwdq($s, @d)
1282              
1283             Layout data
1284              
1285             Parameter Description
1286             1 $s Element size
1287             2 @d Data to be laid out
1288              
1289             =head2 Db(@bytes)
1290              
1291             Layout bytes in the data segment and return their label
1292              
1293             Parameter Description
1294             1 @bytes Bytes to layout
1295              
1296             =head2 Dw(@words)
1297              
1298             Layout words in the data segment and return their label
1299              
1300             Parameter Description
1301             1 @words Words to layout
1302              
1303             =head2 Dd(@dwords)
1304              
1305             Layout double words in the data segment and return their label
1306              
1307             Parameter Description
1308             1 @dwords Double words to layout
1309              
1310             =head2 Dq(@qwords)
1311              
1312             Layout quad words in the data segment and return their label
1313              
1314             Parameter Description
1315             1 @qwords Quad words to layout
1316              
1317             =head2 Rbwdq($s, @d)
1318              
1319             Layout data
1320              
1321             Parameter Description
1322             1 $s Element size
1323             2 @d Data to be laid out
1324              
1325             =head2 Rb(@bytes)
1326              
1327             Layout bytes in the data segment and return their label
1328              
1329             Parameter Description
1330             1 @bytes Bytes to layout
1331              
1332             =head2 Rw(@words)
1333              
1334             Layout words in the data segment and return their label
1335              
1336             Parameter Description
1337             1 @words Words to layout
1338              
1339             =head2 Rd(@dwords)
1340              
1341             Layout double words in the data segment and return their label
1342              
1343             Parameter Description
1344             1 @dwords Double words to layout
1345              
1346             =head2 Rq(@qwords)
1347              
1348             Layout quad words in the data segment and return their label
1349              
1350             Parameter Description
1351             1 @qwords Quad words to layout
1352              
1353             =head2 Comment(@comment)
1354              
1355             Insert a comment into the assembly code
1356              
1357             Parameter Description
1358             1 @comment Text of comment
1359              
1360             B
1361              
1362              
1363             Start;
1364              
1365             Comment "Print a string from memory"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1366              
1367             my $s = "Hello World";
1368             Mov rax, Rs($s);
1369             Mov rdi, length $s;
1370             PrintOutMemory;
1371             Exit;
1372             ok assemble =~ m(Hello World);
1373              
1374              
1375             =head1 Registers
1376              
1377             Operations on registers
1378              
1379             =head2 SaveFirstFour()
1380              
1381             Save the first 4 parameter registers
1382              
1383              
1384             =head2 RestoreFirstFour()
1385              
1386             Restore the first 4 parameter registers
1387              
1388              
1389             =head2 RestoreFirstFourExceptRax()
1390              
1391             Restore the first 4 parameter registers except rax so it can return its value
1392              
1393              
1394             =head2 SaveFirstSeven()
1395              
1396             Save the first 7 parameter registers
1397              
1398              
1399             =head2 RestoreFirstSeven()
1400              
1401             Restore the first 7 parameter registers
1402              
1403              
1404             =head2 RestoreFirstSevenExceptRax()
1405              
1406             Restore the first 7 parameter registers except rax which is being used to return the result
1407              
1408              
1409             =head2 RestoreFirstSevenExceptRaxAndRdi()
1410              
1411             Restore the first 7 parameter registers except rax and rdi which are being used to return the results
1412              
1413              
1414             =head2 RegisterSize($r)
1415              
1416             Return the size of a register
1417              
1418             Parameter Description
1419             1 $r Register
1420              
1421             =head2 ClearRegisters(@registers)
1422              
1423             Clear registers by setting them to zero
1424              
1425             Parameter Description
1426             1 @registers Registers
1427              
1428             =head1 Structured Programming
1429              
1430             Structured programming constructs
1431              
1432             =head2 If($then, $else)
1433              
1434             If
1435              
1436             Parameter Description
1437             1 $then Then - required
1438             2 $else Else - optional
1439              
1440             B
1441              
1442              
1443             Start;
1444             Mov rax, 0;
1445             Test rax,rax;
1446              
1447             If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1448              
1449             {PrintOutRegisterInHex rax;
1450             } sub
1451             {PrintOutRegisterInHex rbx;
1452             };
1453             Mov rax, 1;
1454             Test rax,rax;
1455              
1456             If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1457              
1458             {PrintOutRegisterInHex rcx;
1459             } sub
1460             {PrintOutRegisterInHex rdx;
1461             };
1462             Exit;
1463             ok assemble() =~ m(rbx.*rcx)s;
1464              
1465              
1466             =head2 For($body, $register, $limit, $increment)
1467              
1468             For
1469              
1470             Parameter Description
1471             1 $body Body
1472             2 $register Register
1473             3 $limit Limit on loop
1474             4 $increment Increment
1475              
1476             B
1477              
1478              
1479             Start; # Start the program
1480              
1481             For # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1482              
1483             {PrintOutRegisterInHex rax
1484             } rax, 16, 1;
1485             Exit; # Return to operating system
1486             my $r = assemble;
1487             ok $r =~ m(( 0000){3} 0000)i;
1488             ok $r =~ m(( 0000){3} 000F)i;
1489              
1490              
1491             =head2 S($body, %options)
1492              
1493             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
1494              
1495             Parameter Description
1496             1 $body Body
1497             2 %options Options.
1498              
1499             =head1 Print
1500              
1501             Print
1502              
1503             =head2 PrintOutNl()
1504              
1505             Write a new line
1506              
1507              
1508             B
1509              
1510              
1511             Start;
1512             Comment "Print a string from memory";
1513             my $s = "Hello World";
1514             Mov rax, Rs($s);
1515             Mov rdi, length $s;
1516             PrintOutMemory;
1517             Exit;
1518             ok assemble =~ m(Hello World);
1519              
1520              
1521             =head2 PrintOutString($string)
1522              
1523             Write a constant string to sysout.
1524              
1525             Parameter Description
1526             1 $string String
1527              
1528             B
1529              
1530              
1531             Start;
1532              
1533             PrintOutString "Hello World"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1534              
1535             Exit;
1536             ok assemble =~ m(Hello World);
1537              
1538              
1539             =head2 PrintOutRaxInHex()
1540              
1541             Write the content of register rax to stderr in hexadecimal in big endian notation
1542              
1543              
1544             B
1545              
1546              
1547             Start;
1548             my $q = Rs('abababab');
1549             Mov(rax, "[$q]");
1550             PrintOutString "rax: ";
1551              
1552             PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1553              
1554             PrintOutNl;
1555             Xor rax, rax;
1556             PrintOutString "rax: ";
1557              
1558             PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1559              
1560             PrintOutNl;
1561             Exit;
1562             ok assemble() =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;
1563              
1564              
1565             =head2 ReverseBytesInRax()
1566              
1567             Reverse the bytes in rax
1568              
1569              
1570             =head2 PrintOutRaxInReverseInHex()
1571              
1572             Write the content of register rax to stderr in hexadecimal in little endian notation
1573              
1574              
1575             B
1576              
1577              
1578             Start;
1579             Mov rax, 0x88776655;
1580             Shl rax, 32;
1581             Or rax, 0x44332211;
1582             PrintOutRaxInHex;
1583              
1584             PrintOutRaxInReverseInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1585              
1586             Exit;
1587             ok assemble =~ m(8877 6655 4433 2211 1122 3344 5566 7788)s;
1588              
1589              
1590             =head2 PrintOutRegisterInHex($r)
1591              
1592             Print any register as a hex string
1593              
1594             Parameter Description
1595             1 $r Name of the register to print
1596              
1597             B
1598              
1599              
1600             Start;
1601             my $q = Rs(('a'..'p')x4);
1602             Mov r8,"[$q]";
1603              
1604             PrintOutRegisterInHex r8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1605              
1606             Exit;
1607             ok assemble() =~ m(r8: 6867 6665 6463 6261)s;
1608              
1609              
1610             =head2 PrintOutRipInHex()
1611              
1612             Print the instruction pointer in hex
1613              
1614              
1615             =head2 PrintOutRflagsInHex()
1616              
1617             Print the flags register in hex
1618              
1619              
1620             =head2 PrintOutRegistersInHex()
1621              
1622             Print the general purpose registers in hex
1623              
1624              
1625             B
1626              
1627              
1628             Start;
1629             my $q = Rs('abababab');
1630             Mov(rax, 1);
1631             Mov(rbx, 2);
1632             Mov(rcx, 3);
1633             Mov(rdx, 4);
1634             Mov(r8, 5);
1635             Lea r9, "[rax+rbx]";
1636              
1637             PrintOutRegistersInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1638              
1639             Exit;
1640             my $r = assemble();
1641             ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
1642             ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;
1643              
1644              
1645             =head1 Processes
1646              
1647             Create and manage processes
1648              
1649             =head2 Fork()
1650              
1651             Fork
1652              
1653              
1654             B
1655              
1656              
1657             Start; # Start the program
1658              
1659             Fork; # Fork # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1660              
1661              
1662             Test rax,rax;
1663             If # Parent
1664             {Mov rbx, rax;
1665             WaitPid;
1666             PrintOutRegisterInHex rax;
1667             PrintOutRegisterInHex rbx;
1668             GetPid; # Pid of parent as seen in parent
1669             Mov rcx,rax;
1670             PrintOutRegisterInHex rcx;
1671             }
1672             sub # Child
1673             {Mov r8,rax;
1674             PrintOutRegisterInHex r8;
1675             GetPid; # Child pid as seen in child
1676             Mov r9,rax;
1677             PrintOutRegisterInHex r9;
1678             GetPPid; # Parent pid as seen in child
1679             Mov r10,rax;
1680             PrintOutRegisterInHex r10;
1681             };
1682              
1683             Exit; # Return to operating system
1684              
1685             my $r = assemble();
1686              
1687             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1688             # r9: 0000 0000 0003 0C63 #2 Pid of child
1689             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1690             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1691             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1692             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1693              
1694             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1695             {ok $2 eq $4;
1696             ok $2 eq $5;
1697             ok $3 eq $6;
1698             ok $2 gt $6;
1699             }
1700              
1701             Start; # Start the program
1702             GetUid; # Userid
1703             PrintOutRegisterInHex rax;
1704             Exit; # Return to operating system
1705             my $r = assemble();
1706             ok $r =~ m(rax:( 0000){3});
1707              
1708              
1709             =head2 GetPid()
1710              
1711             Get process identifier
1712              
1713              
1714             B
1715              
1716              
1717             Start; # Start the program
1718             Fork; # Fork
1719              
1720             Test rax,rax;
1721             If # Parent
1722             {Mov rbx, rax;
1723             WaitPid;
1724             PrintOutRegisterInHex rax;
1725             PrintOutRegisterInHex rbx;
1726              
1727             GetPid; # Pid of parent as seen in parent # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1728              
1729             Mov rcx,rax;
1730             PrintOutRegisterInHex rcx;
1731             }
1732             sub # Child
1733             {Mov r8,rax;
1734             PrintOutRegisterInHex r8;
1735              
1736             GetPid; # Child pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1737              
1738             Mov r9,rax;
1739             PrintOutRegisterInHex r9;
1740             GetPPid; # Parent pid as seen in child
1741             Mov r10,rax;
1742             PrintOutRegisterInHex r10;
1743             };
1744              
1745             Exit; # Return to operating system
1746              
1747             my $r = assemble();
1748              
1749             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1750             # r9: 0000 0000 0003 0C63 #2 Pid of child
1751             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1752             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1753             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1754             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1755              
1756             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1757             {ok $2 eq $4;
1758             ok $2 eq $5;
1759             ok $3 eq $6;
1760             ok $2 gt $6;
1761             }
1762              
1763             Start; # Start the program
1764             GetUid; # Userid
1765             PrintOutRegisterInHex rax;
1766             Exit; # Return to operating system
1767             my $r = assemble();
1768             ok $r =~ m(rax:( 0000){3});
1769              
1770              
1771             =head2 GetPPid()
1772              
1773             Get parent process identifier
1774              
1775              
1776             B
1777              
1778              
1779             Start; # Start the program
1780             Fork; # Fork
1781              
1782             Test rax,rax;
1783             If # Parent
1784             {Mov rbx, rax;
1785             WaitPid;
1786             PrintOutRegisterInHex rax;
1787             PrintOutRegisterInHex rbx;
1788             GetPid; # Pid of parent as seen in parent
1789             Mov rcx,rax;
1790             PrintOutRegisterInHex rcx;
1791             }
1792             sub # Child
1793             {Mov r8,rax;
1794             PrintOutRegisterInHex r8;
1795             GetPid; # Child pid as seen in child
1796             Mov r9,rax;
1797             PrintOutRegisterInHex r9;
1798              
1799             GetPPid; # Parent pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1800              
1801             Mov r10,rax;
1802             PrintOutRegisterInHex r10;
1803             };
1804              
1805             Exit; # Return to operating system
1806              
1807             my $r = assemble();
1808              
1809             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1810             # r9: 0000 0000 0003 0C63 #2 Pid of child
1811             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1812             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1813             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1814             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1815              
1816             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1817             {ok $2 eq $4;
1818             ok $2 eq $5;
1819             ok $3 eq $6;
1820             ok $2 gt $6;
1821             }
1822              
1823             Start; # Start the program
1824             GetUid; # Userid
1825             PrintOutRegisterInHex rax;
1826             Exit; # Return to operating system
1827             my $r = assemble();
1828             ok $r =~ m(rax:( 0000){3});
1829              
1830              
1831             =head2 GetUid()
1832              
1833             Get userid of current process
1834              
1835              
1836             =head2 WaitPid()
1837              
1838             Wait for the pid in rax to complete
1839              
1840              
1841             B
1842              
1843              
1844             Start; # Start the program
1845             Fork; # Fork
1846              
1847             Test rax,rax;
1848             If # Parent
1849             {Mov rbx, rax;
1850              
1851             WaitPid; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1852              
1853             PrintOutRegisterInHex rax;
1854             PrintOutRegisterInHex rbx;
1855             GetPid; # Pid of parent as seen in parent
1856             Mov rcx,rax;
1857             PrintOutRegisterInHex rcx;
1858             }
1859             sub # Child
1860             {Mov r8,rax;
1861             PrintOutRegisterInHex r8;
1862             GetPid; # Child pid as seen in child
1863             Mov r9,rax;
1864             PrintOutRegisterInHex r9;
1865             GetPPid; # Parent pid as seen in child
1866             Mov r10,rax;
1867             PrintOutRegisterInHex r10;
1868             };
1869              
1870             Exit; # Return to operating system
1871              
1872             my $r = assemble();
1873              
1874             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1875             # r9: 0000 0000 0003 0C63 #2 Pid of child
1876             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1877             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1878             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1879             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1880              
1881             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1882             {ok $2 eq $4;
1883             ok $2 eq $5;
1884             ok $3 eq $6;
1885             ok $2 gt $6;
1886             }
1887              
1888             Start; # Start the program
1889             GetUid; # Userid
1890             PrintOutRegisterInHex rax;
1891             Exit; # Return to operating system
1892             my $r = assemble();
1893             ok $r =~ m(rax:( 0000){3});
1894              
1895              
1896             =head2 ReadTimeStampCounter()
1897              
1898             Read the time stamp counter
1899              
1900              
1901             B
1902              
1903              
1904             Start;
1905             for(1..10)
1906              
1907             {ReadTimeStampCounter; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1908              
1909             PrintOutRegisterInHex rax;
1910             }
1911             Exit;
1912             my @s = split /
1913             /, assemble();
1914             my @S = sort @s;
1915             is_deeply \@s, \@S;
1916              
1917              
1918             =head1 Stack
1919              
1920             Manage data on the stack
1921              
1922             =head2 Push, Pop, Peek
1923              
1924             Generic versions of push, pop, peek
1925              
1926             =head3 PushR(@r)
1927              
1928             Push registers onto the stack
1929              
1930             Parameter Description
1931             1 @r Register
1932              
1933             =head3 PopR(@r)
1934              
1935             Pop registers from the stack
1936              
1937             Parameter Description
1938             1 @r Register
1939              
1940             B
1941              
1942              
1943             Start;
1944             my $q = Rs my $s = join '', ('a'..'p')x4;;
1945             Mov rax, Ds('0'x128);
1946              
1947             Vmovdqu32 zmm0, "[$q]";
1948             Vprolq zmm1, zmm0, 32;
1949             Vmovdqu32 "[rax]", zmm1;
1950              
1951             Mov rdi, length $s;
1952             PrintOutMemory;
1953             Exit;
1954              
1955             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
1956             ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
1957              
1958              
1959             =head3 PeekR($r)
1960              
1961             Peek at register on stack
1962              
1963             Parameter Description
1964             1 $r Register
1965              
1966             =head2 Declarations
1967              
1968             Declare local varibles in a frame on the stack
1969              
1970             =head3 LocalData()
1971              
1972             Map local data
1973              
1974              
1975             =head3 LocalData::start($local)
1976              
1977             Start a local data area on the stack
1978              
1979             Parameter Description
1980             1 $local Local data descriptor
1981              
1982             =head3 LocalData::free($local)
1983              
1984             Free a local data area on the stack
1985              
1986             Parameter Description
1987             1 $local Local data descriptor
1988              
1989             =head3 LocalData::variable($local, $length, $comment)
1990              
1991             Add a local variable
1992              
1993             Parameter Description
1994             1 $local Local data descriptor
1995             2 $length Length of data
1996             3 $comment Optional comment
1997              
1998             =head3 LocalVariable::stack($variable)
1999              
2000             Address a local variable on the stack
2001              
2002             Parameter Description
2003             1 $variable Variable
2004              
2005             =head3 LocalData::allocate8($local, @comments)
2006              
2007             Add some 8 byte local variables and return an array of variable definitions
2008              
2009             Parameter Description
2010             1 $local Local data descriptor
2011             2 @comments Optional comment
2012              
2013             =head3 AllocateAll8OnStack($N)
2014              
2015             Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions...)
2016              
2017             Parameter Description
2018             1 $N Number of variables required
2019              
2020             =head1 Memory
2021              
2022             Allocate and print memory
2023              
2024             =head2 PrintOutMemoryInHex()
2025              
2026             Dump memory from the address in rax for the length in rdi
2027              
2028              
2029             =head2 PrintOutMemory()
2030              
2031             Print the memory addressed by rax for a length of rdi
2032              
2033              
2034             =head2 AllocateMemory()
2035              
2036             Allocate the amount of memory specified in rax via mmap and return the address of the allocated memory in rax
2037              
2038              
2039             B
2040              
2041              
2042             Start;
2043             my $N = 2048;
2044             my $q = Rs('a'..'p');
2045             Mov rax, $N;
2046              
2047             AllocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2048              
2049             PrintOutRegisterInHex rax;
2050              
2051             Vmovdqu8 xmm0, "[$q]";
2052             Vmovdqu8 "[rax]", xmm0;
2053             Mov rdi,16;
2054             PrintOutMemory;
2055             PrintOutNl;
2056              
2057             Mov rdi, $N;
2058             FreeMemory;
2059             PrintOutRegisterInHex rax;
2060             Exit;
2061             ok assemble() =~ m(abcdefghijklmnop)s;
2062              
2063             Start;
2064             my $N = 4096;
2065             my $S = RegisterSize rax;
2066             Mov rax, $N;
2067              
2068             AllocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2069              
2070             PrintOutRegisterInHex rax;
2071             Mov rdi, $N;
2072             MemoryClear;
2073             PrintOutRegisterInHex rax;
2074             PrintOutMemoryInHex;
2075             Exit;
2076              
2077             my $r = assemble;
2078             if ($r =~ m((0000.*0000))s)
2079             {is_deeply length($1), 10269;
2080             }
2081              
2082              
2083             =head2 FreeMemory()
2084              
2085             Free memory via mmap. The address of the memory is in rax, the length to free is in rdi
2086              
2087              
2088             B
2089              
2090              
2091             Start;
2092             my $N = 2048;
2093             my $q = Rs('a'..'p');
2094             Mov rax, $N;
2095             AllocateMemory;
2096             PrintOutRegisterInHex rax;
2097              
2098             Vmovdqu8 xmm0, "[$q]";
2099             Vmovdqu8 "[rax]", xmm0;
2100             Mov rdi,16;
2101             PrintOutMemory;
2102             PrintOutNl;
2103              
2104             Mov rdi, $N;
2105              
2106             FreeMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2107              
2108             PrintOutRegisterInHex rax;
2109             Exit;
2110             ok assemble() =~ m(abcdefghijklmnop)s;
2111              
2112             Start;
2113             my $N = 4096;
2114             my $S = RegisterSize rax;
2115             Mov rax, $N;
2116             AllocateMemory;
2117             PrintOutRegisterInHex rax;
2118             Mov rdi, $N;
2119             MemoryClear;
2120             PrintOutRegisterInHex rax;
2121             PrintOutMemoryInHex;
2122             Exit;
2123              
2124             my $r = assemble;
2125             if ($r =~ m((0000.*0000))s)
2126             {is_deeply length($1), 10269;
2127             }
2128              
2129              
2130             =head2 MemoryClear()
2131              
2132             Clear memory - the address of the memory is in rax, the length in rdi
2133              
2134              
2135             =head1 Files
2136              
2137             Process a file
2138              
2139             =head2 OpenRead()
2140              
2141             Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
2142              
2143              
2144             B
2145              
2146              
2147             Start; # Start the program
2148             Mov rax, Rs($0); # File to stat
2149              
2150             OpenRead; # Open file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2151              
2152             PrintOutRegisterInHex rax;
2153             Close(rax); # Close file
2154             PrintOutRegisterInHex rax;
2155             Exit; # Return to operating system
2156             my $r = assemble();
2157             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
2158             ok $r =~ m(( 0000){4})i; # Expected file number
2159              
2160              
2161             =head2 Close($fdes)
2162              
2163             Close a file descriptor
2164              
2165             Parameter Description
2166             1 $fdes File descriptor
2167              
2168             B
2169              
2170              
2171             Start; # Start the program
2172             Mov rax, Rs($0); # File to stat
2173             OpenRead; # Open file
2174             PrintOutRegisterInHex rax;
2175              
2176             Close(rax); # Close file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2177              
2178             PrintOutRegisterInHex rax;
2179             Exit; # Return to operating system
2180             my $r = assemble();
2181             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
2182             ok $r =~ m(( 0000){4})i; # Expected file number
2183              
2184              
2185             =head2 StatSize()
2186              
2187             Stat a file whose name is addressed by rax to get its size in rax
2188              
2189              
2190             B
2191              
2192              
2193             Start; # Start the program
2194             Mov rax, Rs($0); # File to stat
2195              
2196             StatSize; # Stat the file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2197              
2198             PrintOutRegisterInHex rax;
2199             Exit; # Return to operating system
2200             my $r = assemble() =~ s( ) ()gsr;
2201             if ($r =~ m(rax:([0-9a-f]{16}))is) # Compare file size obtained with that from fileSize()
2202             {is_deeply $1, sprintf("%016X", fileSize($0));
2203             }
2204              
2205              
2206             =head2 ReadFile()
2207              
2208             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
2209              
2210              
2211             B
2212              
2213              
2214             Start; # Start the program
2215             Mov rax, Rs($0); # File to read
2216              
2217             ReadFile; # Read file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2218              
2219             PrintOutMemory; # Print memory
2220             Exit; # Return to operating system
2221             my $r = assemble(); # Assemble and execute
2222             ok index($r =~ s([^0x0-0x7f]) ()gsr, readFile($0) =~ s([^0x0-0x7f]) ()gsr)>-1;# Output contains this file
2223              
2224              
2225             =head1 Assemble
2226              
2227             Assemble generated code
2228              
2229             =head2 Start()
2230              
2231             Initialize the assembler
2232              
2233              
2234             B
2235              
2236              
2237              
2238             Start; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2239              
2240             PrintOutString "Hello World";
2241             Exit;
2242             ok assemble =~ m(Hello World);
2243              
2244              
2245             =head2 Exit($c)
2246              
2247             Exit with the specified return code or zero if no return code supplied
2248              
2249             Parameter Description
2250             1 $c Return code
2251              
2252             B
2253              
2254              
2255             Start;
2256             PrintOutString "Hello World";
2257              
2258             Exit; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2259              
2260             ok assemble =~ m(Hello World);
2261              
2262              
2263             =head2 assemble(%options)
2264              
2265             Assemble the generated code
2266              
2267             Parameter Description
2268             1 %options Options
2269              
2270             B
2271              
2272              
2273             Start;
2274             PrintOutString "Hello World";
2275             Exit;
2276              
2277             ok assemble =~ m(Hello World); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2278              
2279              
2280              
2281              
2282             =head1 Private Methods
2283              
2284             =head2 label()
2285              
2286             Create a unique label
2287              
2288              
2289              
2290             =head1 Index
2291              
2292              
2293             1 L - Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions.
2294              
2295             2 L - Allocate the amount of memory specified in rax via mmap and return the address of the allocated memory in rax
2296              
2297             3 L - Assemble the generated code
2298              
2299             4 L - Clear registers by setting them to zero
2300              
2301             5 L - Close a file descriptor
2302              
2303             6 L - Insert a comment into the assembly code
2304              
2305             7 L - Layout bytes in the data segment and return their label
2306              
2307             8 L - Layout data
2308              
2309             9 L - Layout double words in the data segment and return their lab
2310              
2311             10 L - Layout quad words in the data segment and return their label
2312              
2313             11 L - Layout bytes in memory and return their label
2314              
2315             12 L - Layout words in the data segment and return their label
2316              
2317             13 L - Exit with the specified return code or zero if no return code supplied
2318              
2319             14 L - For
2320              
2321             15 L - Fork
2322              
2323             16 L - Free memory via mmap.
2324              
2325             17 L - Get process identifier
2326              
2327             18 L - Get parent process identifier
2328              
2329             19 L - Get userid of current process
2330              
2331             20 L - If
2332              
2333             21 L - Create a unique label
2334              
2335             22 L - Map local data
2336              
2337             23 L - Add some 8 byte local variables and return an array of variable definitions
2338              
2339             24 L - Free a local data area on the stack
2340              
2341             25 L - Start a local data area on the stack
2342              
2343             26 L - Add a local variable
2344              
2345             27 L - Address a local variable on the stack
2346              
2347             28 L - Clear memory - the address of the memory is in rax, the length in rdi
2348              
2349             29 L - Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
2350              
2351             30 L - Peek at register on stack
2352              
2353             31 L - Pop registers from the stack
2354              
2355             32 L - Print the memory addressed by rax for a length of rdi
2356              
2357             33 L - Dump memory from the address in rax for the length in rdi
2358              
2359             34 L - Write a new line
2360              
2361             35 L - Write the content of register rax to stderr in hexadecimal in big endian notation
2362              
2363             36 L - Write the content of register rax to stderr in hexadecimal in little endian notation
2364              
2365             37 L - Print any register as a hex string
2366              
2367             38 L - Print the general purpose registers in hex
2368              
2369             39 L - Print the flags register in hex
2370              
2371             40 L - Print the instruction pointer in hex
2372              
2373             41 L - Write a constant string to sysout.
2374              
2375             42 L - Push registers onto the stack
2376              
2377             43 L - Layout bytes in the data segment and return their label
2378              
2379             44 L - Layout data
2380              
2381             45 L - Layout double words in the data segment and return their label
2382              
2383             46 L - Read a file whose name is addressed by rax into memory.
2384              
2385             47 L - Read the time stamp counter
2386              
2387             48 L - Return the size of a register
2388              
2389             49 L - Restore the first 4 parameter registers
2390              
2391             50 L - Restore the first 4 parameter registers except rax so it can return its value
2392              
2393             51 L - Restore the first 7 parameter registers
2394              
2395             52 L - Restore the first 7 parameter registers except rax which is being used to return the result
2396              
2397             53 L - Restore the first 7 parameter registers except rax and rdi which are being used to return the results
2398              
2399             54 L - Reverse the bytes in rax
2400              
2401             55 L - Layout quad words in the data segment and return their label
2402              
2403             56 L - Layout bytes in read only memory and return their label
2404              
2405             57 L - Layout words in the data segment and return their label
2406              
2407             58 L - 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
2408              
2409             59 L - Save the first 4 parameter registers
2410              
2411             60 L - Save the first 7 parameter registers
2412              
2413             61 L - Set a label in the code section
2414              
2415             62 L - Initialize the assembler
2416              
2417             63 L - Stat a file whose name is addressed by rax to get its size in rax
2418              
2419             64 L - Wait for the pid in rax to complete
2420              
2421             =head1 Installation
2422              
2423             This module is written in 100% Pure Perl and, thus, it is easy to read,
2424             comprehend, use, modify and install via B:
2425              
2426             sudo cpan install Nasm::X86
2427              
2428             =head1 Author
2429              
2430             L
2431              
2432             L
2433              
2434             =head1 Copyright
2435              
2436             Copyright (c) 2016-2021 Philip R Brenan.
2437              
2438             This module is free software. It may be used, redistributed and/or modified
2439             under the same terms as Perl itself.
2440              
2441             =cut
2442              
2443              
2444              
2445             # Tests and documentation
2446              
2447             sub test
2448 0     0 0   {my $p = __PACKAGE__;
2449 0           binmode($_, ":utf8") for *STDOUT, *STDERR;
2450 0 0         return if eval "eof(${p}::DATA)";
2451 0           my $s = eval "join('', <${p}::DATA>)";
2452 0 0         $@ and die $@;
2453 0           eval $s;
2454 0 0         $@ and die $@;
2455 0           1
2456             }
2457              
2458             test unless caller;
2459              
2460             1;
2461             # podDocumentation
2462             #__DATA__
2463 1     1   8 use Time::HiRes qw(time);
  1         3  
  1         10  
2464 1     1   922 use Test::More;
  1         67188  
  1         10  
2465              
2466             my $localTest = ((caller(1))[0]//'Nasm::X86') eq "Nasm::X86"; # Local testing mode
2467              
2468             Test::More->builder->output("/dev/null") if $localTest; # Reduce number of confirmation messages during testing
2469              
2470             $ENV{PATH} = $ENV{PATH}.":/var/isde:sde"; # Intel emulator
2471              
2472             if ($^O =~ m(bsd|linux)i) # Supported systems
2473             {if (confirmHasCommandLineCommand(q(nasm)) and # Network assembler
2474             confirmHasCommandLineCommand(q(sde64))) # Intel emulator
2475             {plan tests => 30;
2476             }
2477             else
2478             {plan skip_all =>qq(Nasm or Intel 64 emulator not available);
2479             }
2480             }
2481             else
2482             {plan skip_all =>qq(Not supported on: $^O);
2483             }
2484              
2485             my $start = time; # Tests
2486              
2487             #goto latest;
2488              
2489             if (1) { #TExit #TPrintOutString #Tassemble #TStart
2490             Start;
2491             PrintOutString "Hello World";
2492             Exit;
2493             ok Assemble =~ m(Hello World);
2494             }
2495              
2496             if (1) { #TMov #TComment #TRs #TPrintOutNl
2497             Start;
2498             Comment "Print a string from memory";
2499             my $s = "Hello World";
2500             Mov rax, Rs($s);
2501             Mov rdi, length $s;
2502             PrintOutMemory;
2503             Exit;
2504             ok Assemble =~ m(Hello World);
2505             }
2506              
2507             if (1) { #TPrintOutRaxInHex #TXor
2508             Start;
2509             my $q = Rs('abababab');
2510             Mov(rax, "[$q]");
2511             PrintOutString "rax: ";
2512             PrintOutRaxInHex;
2513             PrintOutNl;
2514             Xor rax, rax;
2515             PrintOutString "rax: ";
2516             PrintOutRaxInHex;
2517             PrintOutNl;
2518             Exit;
2519             ok Assemble =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;
2520             }
2521              
2522             if (1) { #TPrintOutRegistersInHex #TLea
2523             Start;
2524             my $q = Rs('abababab');
2525             Mov(rax, 1);
2526             Mov(rbx, 2);
2527             Mov(rcx, 3);
2528             Mov(rdx, 4);
2529             Mov(r8, 5);
2530             Lea r9, "[rax+rbx]";
2531             PrintOutRegistersInHex;
2532             Exit;
2533             my $r = Assemble;
2534             ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
2535             ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;
2536             }
2537              
2538             if (1) { #TVmovdqu32 #TVprolq #TDs
2539             Start;
2540             my $q = Rs('a'..'z');
2541             Mov rax, Ds('0'x64); # Output area
2542             Vmovdqu32(xmm0, "[$q]"); # Load
2543             Vprolq (xmm0, xmm0, 32); # Rotate double words in quad words
2544             Vmovdqu32("[rax]", xmm0); # Save
2545             Mov rdi, 16;
2546             PrintOutMemory;
2547             Exit;
2548             ok Assemble =~ m(efghabcdmnopijkl)s;
2549             }
2550              
2551             if (1) {
2552             Start;
2553             my $q = Rs(('a'..'p')x2);
2554             Mov rax, Ds('0'x64);
2555             Vmovdqu32(ymm0, "[$q]");
2556             Vprolq (ymm0, ymm0, 32);
2557             Vmovdqu32("[rax]", ymm0);
2558             Mov rdi, 32;
2559             PrintOutMemory;
2560             Exit;
2561             ok Assemble =~ m(efghabcdmnopijklefghabcdmnopijkl)s;
2562             }
2563              
2564             if (1) { #TPopR #TVmovdqu64
2565             Start;
2566             my $q = Rs my $s = join '', ('a'..'p')x4;;
2567             Mov rax, Ds('0'x128);
2568              
2569             Vmovdqu32 zmm0, "[$q]";
2570             Vprolq zmm1, zmm0, 32;
2571             Vmovdqu32 "[rax]", zmm1;
2572              
2573             Mov rdi, length $s;
2574             PrintOutMemory;
2575             Exit;
2576              
2577             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
2578             ok Assemble =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
2579             }
2580              
2581             if (1) { #TPrintOutRegisterInHex
2582             Start;
2583             my $q = Rs(('a'..'p')x4);
2584             Mov r8,"[$q]";
2585             PrintOutRegisterInHex r8;
2586             Exit;
2587             ok Assemble =~ m(r8: 6867 6665 6463 6261)s;
2588             }
2589              
2590             if (1) { #TVmovdqu8
2591             Start;
2592             my $q = Rs('a'..'p');
2593             Vmovdqu8 xmm0, "[$q]";
2594             PrintOutRegisterInHex xmm0;
2595             Exit;
2596             ok Assemble =~ m(xmm0: 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2597             }
2598              
2599             if (1) {
2600             Start;
2601             my $q = Rs('a'..'p', 'A'..'P', );
2602             Vmovdqu8 ymm0, "[$q]";
2603             PrintOutRegisterInHex ymm0;
2604             Exit;
2605             ok Assemble =~ m(ymm0: 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2606             }
2607              
2608             if (1) {
2609             Start;
2610             my $q = Rs(('a'..'p', 'A'..'P') x 2);
2611             Vmovdqu8 zmm0, "[$q]";
2612             PrintOutRegisterInHex zmm0;
2613             Exit;
2614             ok Assemble =~ m(zmm0: 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2615             }
2616              
2617             if (1) { #TAllocateMemory #TFreeMemory
2618             Start;
2619             my $N = 2048;
2620             my $q = Rs('a'..'p');
2621             Mov rax, $N;
2622             AllocateMemory;
2623             PrintOutRegisterInHex rax;
2624              
2625             Vmovdqu8 xmm0, "[$q]";
2626             Vmovdqu8 "[rax]", xmm0;
2627             Mov rdi,16;
2628             PrintOutMemory;
2629             PrintOutNl;
2630              
2631             Mov rdi, $N;
2632             FreeMemory;
2633             PrintOutRegisterInHex rax;
2634             Exit;
2635             ok Assemble =~ m(abcdefghijklmnop)s;
2636             }
2637              
2638             if (1) { #TReadTimeStampCounter
2639             Start;
2640             for(1..10)
2641             {ReadTimeStampCounter;
2642             PrintOutRegisterInHex rax;
2643             }
2644             Exit;
2645             my @s = split /\n/, Assemble;
2646             my @S = sort @s;
2647             is_deeply \@s, \@S;
2648             }
2649              
2650             if (1) { #TIf
2651             Start;
2652             Mov rax, 0;
2653             Test rax,rax;
2654             If
2655             {PrintOutRegisterInHex rax;
2656             } sub
2657             {PrintOutRegisterInHex rbx;
2658             };
2659             Mov rax, 1;
2660             Test rax,rax;
2661             If
2662             {PrintOutRegisterInHex rcx;
2663             } sub
2664             {PrintOutRegisterInHex rdx;
2665             };
2666             Exit;
2667             ok Assemble =~ m(rbx.*rcx)s;
2668             }
2669              
2670             if (1) { #TFork #TGetPid #TGetPPid #TWaitPid
2671             Start; # Start the program
2672             Fork; # Fork
2673              
2674             Test rax,rax;
2675             If # Parent
2676             {Mov rbx, rax;
2677             WaitPid;
2678             PrintOutRegisterInHex rax;
2679             PrintOutRegisterInHex rbx;
2680             GetPid; # Pid of parent as seen in parent
2681             Mov rcx,rax;
2682             PrintOutRegisterInHex rcx;
2683             }
2684             sub # Child
2685             {Mov r8,rax;
2686             PrintOutRegisterInHex r8;
2687             GetPid; # Child pid as seen in child
2688             Mov r9,rax;
2689             PrintOutRegisterInHex r9;
2690             GetPPid; # Parent pid as seen in child
2691             Mov r10,rax;
2692             PrintOutRegisterInHex r10;
2693             };
2694              
2695             Exit; # Return to operating system
2696              
2697             my $r = Assemble;
2698              
2699             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
2700             # r9: 0000 0000 0003 0C63 #2 Pid of child
2701             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
2702             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
2703             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
2704             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
2705              
2706             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
2707             {ok $2 eq $4;
2708             ok $2 eq $5;
2709             ok $3 eq $6;
2710             ok $2 gt $6;
2711             }
2712             }
2713              
2714             if (1) { #TFork #TGetPid #TGetPPid #TWaitPid
2715             Start; # Start the program
2716             GetUid; # Userid
2717             PrintOutRegisterInHex rax;
2718             Exit; # Return to operating system
2719             my $r = Assemble;
2720             ok $r =~ m(rax:( 0000){3});
2721             }
2722              
2723             if (1) { #TStatSize
2724             Start; # Start the program
2725             Mov rax, Rs($0); # File to stat
2726             StatSize; # Stat the file
2727             PrintOutRegisterInHex rax;
2728             Exit; # Return to operating system
2729             my $r = Assemble =~ s( ) ()gsr;
2730             if ($r =~ m(rax:([0-9a-f]{16}))is) # Compare file size obtained with that from fileSize()
2731             {is_deeply $1, sprintf("%016X", fileSize($0));
2732             }
2733             }
2734              
2735             if (1) { #TOpenRead #TClose
2736             Start; # Start the program
2737             Mov rax, Rs($0); # File to stat
2738             OpenRead; # Open file
2739             PrintOutRegisterInHex rax;
2740             Close(rax); # Close file
2741             PrintOutRegisterInHex rax;
2742             Exit; # Return to operating system
2743             my $r = Assemble;
2744             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
2745             ok $r =~ m(( 0000){4})i; # Expected file number
2746             }
2747              
2748             if (1) { #TFor
2749             Start; # Start the program
2750             For
2751             {PrintOutRegisterInHex rax
2752             } rax, 16, 1;
2753             Exit; # Return to operating system
2754             my $r = Assemble;
2755             ok $r =~ m(( 0000){3} 0000)i;
2756             ok $r =~ m(( 0000){3} 000F)i;
2757             }
2758              
2759             if (1) { #TPrintOutRaxInReverseInHex
2760             Start;
2761             Mov rax, 0x88776655;
2762             Shl rax, 32;
2763             Or rax, 0x44332211;
2764             PrintOutRaxInHex;
2765             PrintOutRaxInReverseInHex;
2766             Exit;
2767             ok Assemble =~ m(8877 6655 4433 2211 1122 3344 5566 7788)s;
2768             }
2769              
2770             if (1) { #TAllocateMemory #TFreeMemory
2771             Start;
2772             my $N = 4096;
2773             my $S = RegisterSize rax;
2774             Mov rax, $N;
2775             AllocateMemory;
2776             PrintOutRegisterInHex rax;
2777             Mov rdi, $N;
2778             MemoryClear;
2779             PrintOutRegisterInHex rax;
2780             PrintOutMemoryInHex;
2781             Exit;
2782              
2783             my $r = Assemble;
2784             if ($r =~ m((0000.*0000))s)
2785             {is_deeply length($1), 10289;
2786             }
2787             }
2788              
2789             if (1) { #TCall #TS
2790             Start;
2791             Mov rax, 0x44332211;
2792             PrintOutRegisterInHex rax;
2793              
2794             my $s = S
2795             {PrintOutRegisterInHex rax;
2796             Inc rax;
2797             PrintOutRegisterInHex rax;
2798             };
2799              
2800             Call $s;
2801              
2802             PrintOutRegisterInHex rax;
2803             Exit;
2804             my $r = Assemble;
2805             ok $r =~ m(0000 0000 4433 2211.*2211.*2212.*0000 0000 4433 2212)s;
2806             }
2807              
2808             if (1) { #TReadFile #TPrintMemory
2809             Start; # Start the program
2810             Mov rax, Rs($0); # File to read
2811             ReadFile; # Read file
2812             PrintOutMemory; # Print memory
2813             Exit; # Return to operating system
2814             my $r = Assemble; # Assemble and execute
2815             ok index($r =~ s([^0x0-0x7f]) ()gsr, readFile($0) =~ s([^0x0-0x7f]) ()gsr)>-1;# Output contains this file
2816             }
2817              
2818             latest:;
2819              
2820             if (1) { #TCreateByteString
2821             Start; # Start the program
2822             my $s = CreateByteString; # Create a string
2823             Mov rdi, 0x68676665; # Load a string to append
2824             Shl rdi, 32;
2825             Or rdi, 0x64636261;
2826             $s->ar; # Add a string held in a register
2827             $s->ar;
2828             $s->out; # Print byte string
2829             Exit; # Return to operating system
2830             Assemble =~ m(abcdefghabcdefgh); # Assemble and execute
2831             }
2832              
2833             lll "Finished:", time - $start;