File Coverage

blib/lib/Nasm/X86.pm
Criterion Covered Total %
statement 99 996 9.9
branch 6 202 2.9
condition 0 8 0.0
subroutine 12 343 3.5
pod 65 308 21.1
total 182 1857 9.8


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