summary refs log tree commit diff stats
path: root/pictor.pl
Commit message (Expand)AuthorAgeFilesLines
* Remove support for 'is'Andinus2020-06-111-11/+1
* Remove feature 'say'Andinus2020-06-111-1/+0
* Require OpenBSD:: only on OpenBSD, re-style codeAndinus2020-06-111-23/+33
* Add note about OpenBSD::Pledge & OpenBSD::UnveilAndinus2020-05-101-1/+1
* Replace tab characters with spacesAndinus2020-05-101-14/+14
* Use die instead of warn & exit, simplify the scriptAndinus2020-05-101-19/+18
* Refine pledge permissionsAndinus2020-05-101-3/+9
* Print error message if no match is foundAndinus2020-05-101-2/+10
* Support "pictor is term" syntaxAndinus2020-05-061-0/+6
* Use \t instead of \s & make the output neaterAndinus2020-05-061-3/+5
* Fix variable nameAndinus2020-05-061-5/+5
* Make script executableAndinus2020-05-061-0/+0
* Initial commitAndinus2020-05-061-0/+64
;id=20d1c9057a559ce8db83bbc2787ca91348bcb16f'>20d1c905 ^
5497090a ^

20d1c905 ^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37





                                                                           


                  
             
                             







                   
                                            





                               



                                                      


             

                                 

 
href='#n231'>231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
50
# write: like _write, but also support in-memory output streams (`ostream`s)
# in addition to file descriptors.
#
# Our first dependency-injected and testable primitive. We can pass it either
# a file descriptor or an address to an output stream or ostream. If a file
# descriptor is passed in, we _write to it using the right syscall. If a 'fake
# file descriptor' or ostream is passed in, we append to the output stream.
# This lets us redirect output in tests and check it later.
#
# We assume our data segment will never begin at an address shorter than
# 0x08000000, so any smaller arguments are assumed to be real file descriptors.
#
# An ostream looks like this:
#   write: int  # index at which writes go
#   data: (array byte)  # prefixed by length as usual

== data

# In-memory ostream for tests to write() to.
# Also illustrates the layout of ostreams.
Test-ostream:
  # current write index
  00 00 00 00
  # length (= 8)
  08 00 00 00
  # data
  00 00 00 00 00 00 00 00  # 8 bytes

== code

# instruction                     effective address                                                   operand     displacement    immediate
# op          subop               mod             rm32          base        index         scale       r32
# 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

# main:  (manual test if this is the last file loaded)
  e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
  # syscall(exit, Num-test-failures)
  8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           1/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  b8/copy-to-EAX  1/imm32
  cd/syscall  0x80/imm8

write:  # f : fd or (address stream), s : (address array byte) -> <void>
  # prolog
  55/push-EBP
  89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  # if (f < 0x08000000) _write(f, s), return  # f can't be a user-mode address, so treat it as a kernel file descriptor
  81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
  7f/jump-if-greater  $write:fake/disp8
    # push args
  ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
  ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         .                 # push *(EBP+8)
    # call
  e8/call  _write/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  eb/jump  $write:end/disp8
$write:fake:
  # otherwise, treat 'f' as a stream to append to
  # save registers
  50/push-EAX
  51/push-ECX
  52/push-EDX
  53/push-EBX
  # ECX = f
  8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none              1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
  # EDX = f.write
  8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
  # EBX = f.length
  8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(ECX+4) to EBX
  # EAX = _append(&f.data[f.write], &f.data[f.length], s)
    # push s
  ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
    # push &f.data[f.length]
  8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  3/index/EBX   .           3/r32/EBX   8/disp8         .                 # copy ECX+EBX+8 to EBX
  53/push-EBX
    # push &f.data[f.write]
  8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           3/r32/EBX   8/disp8         .                 # copy ECX+EBX+8 to EBX
  53/push-EBX
    # call
  e8/call  _append/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  # f.write += EAX
  01/add                          0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to *ECX
  # restore registers
  5b/pop-to-EBX
  5a/pop-to-EDX
  59/pop-to-ECX
  58/pop-to-EAX
$write:end:
  # epilog
  89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
  5d/pop-to-EBP
  c3/return

clear-ostream:  # f : (address ostream)
  # prolog
  55/push-EBP
  89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  # save registers
  50/push-EAX
  51/push-ECX
  # EAX = f
  8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none              0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
  # ECX = f.length
  8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
  # ECX = &f.data[f.length]
  8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   8/disp8         .                 # copy EAX+ECX+8 to ECX
  # f.write = 0
  c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  # EAX = f.data
  81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               8/imm32           # add to EAX
  # while (true)
$clear-ostream:loop:
  # if EAX >= ECX break
  39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
  7d/jump-if-greater-or-equal  $clear-ostream:end/disp8
  # *EAX = 0
  c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  # EAX += 4
  81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
  eb/jump  $clear-ostream:loop/disp8
$clear-ostream:end:
  # restore registers
  59/pop-to-ECX
  58/pop-to-EAX
  # epilog
  89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
  5d/pop-to-EBP
  c3/return

test-write-single:
  # clear-ostream(Test-ostream)
    # push args
  68/push  Test-ostream/imm32
    # call
  e8/call  clear-ostream/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  # write(Test-ostream, "Ab")
    # push args
  68/push  "Ab"/imm32
  68/push  Test-ostream/imm32
    # call
  e8/call  write/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  # check-ints-equal(*Test-ostream.data, 41/A 62/b 00 00, msg)
    # push args
  68/push  "F - test-write-single"/imm32
  68/push  0x006241/imm32/Ab
    # push *Test-ostream.data
  b8/copy-to-EAX  Test-ostream/imm32
  ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
    # call
  e8/call  check-ints-equal/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  # end
  c3/return

test-write-appends:
  # clear-ostream(Test-ostream)
    # push args
  68/push  Test-ostream/imm32
    # call
  e8/call  clear-ostream/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  # write(Test-ostream, "C")
    # push args
  68/push  "C"/imm32
  68/push  Test-ostream/imm32
    # call
  e8/call  write/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  # write(Test-ostream, "D")
    # push args
  68/push  "D"/imm32
  68/push  Test-ostream/imm32
    # call
  e8/call  write/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  # check-ints-equal(*Test-ostream.data, 43/C 44/D 00 00, msg)
    # push args
  68/push  "F - test-write-appends"/imm32
  68/push  0x00004443/imm32/C-D
    # push *Test-ostream.data
  b8/copy-to-EAX  Test-ostream/imm32
  ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
    # call
  e8/call  check-ints-equal/disp32
    # discard args
  81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  # end
  c3/return