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;