https://github.com/akkartik/mu/blob/master/subx/apps/pack.subx
   1 # Read a text file of SubX instructions from stdin, and convert it into a list
   2 # of whitespace-separated ascii hex bytes on stdout, suitable to be further
   3 # processed by apps/hex.
   4 #
   5 # To run (from the subx/ directory):
   6 #   $ ./subx translate *.subx apps/pack.subx -o apps/pack
   7 #   $ echo '05/add-to-EAX 0x20/imm32'  |./subx run apps/pack
   8 # Expected output:
   9 #   05 20 00 00 00  # 05/add-to-EAX 0x20/imm32
  10 # The original instruction gets included as a comment at the end of each
  11 # converted line.
  12 #
  13 # There's zero error-checking. For now we assume the input program is valid.
  14 # We'll continue to rely on the C++ version for error messages.
  15 #
  16 # Label definitions and uses are left untouched for a future 'pass'.
  17 
  18 == code
  19 #   instruction                     effective address                                                   register    displacement    immediate
  20 # . op          subop               mod             rm32          base        index         scale       r32
  21 # . 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
  22 
  23     # for debugging: run a single test
  24 #?     e8/call test-emit-non-number-with-metadata/disp32
  25 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  26 #?     eb/jump  $main:end/disp8
  27 
  28 # main: run tests if necessary, convert stdin if not
  29     # . prolog
  30     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  31     # - if argc > 1 and argv[1] == "test" then return run_tests()
  32     # . argc > 1
  33     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  34     7e/jump-if-lesser-or-equal  $run-main/disp8
  35     # . argv[1] == "test"
  36     # . . push args
  37     68/push  "test"/imm32
  38     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  39     # . . call
  40     e8/call  kernel-string-equal?/disp32
  41     # . . discard args
  42     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  43     # . check result
  44     3d/compare-EAX  1/imm32
  45     75/jump-if-not-equal  $run-main/disp8
  46     # . run-tests()
  47     e8/call  run-tests/disp32
  48     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  49     eb/jump  $main:end/disp8
  50 $run-main:
  51     # - otherwise convert stdin
  52     # var ed/EAX : exit-descriptor
  53     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  54     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  55     # configure ed to really exit()
  56     # . ed->target = 0
  57     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  58     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  59     # . . push args
  60     50/push-EAX/ed
  61     68/push  Stderr/imm32
  62     68/push  Stdout/imm32
  63     68/push  Stdin/imm32
  64     # . . call
  65     e8/call  convert/disp32
  66     # . . discard args
  67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  68     # . syscall(exit, 0)
  69     bb/copy-to-EBX  0/imm32
  70 $main:end:
  71     b8/copy-to-EAX  1/imm32/exit
  72     cd/syscall  0x80/imm8
  73 
  74 # - big picture
  75 # We'll operate on each line/instruction in isolation. That way we only need to
  76 # allocate memory for converting a single instruction.
  77 #
  78 # To pack an entire file:
  79 #   skip segment headers
  80 #   pack every instruction in the code segment
  81 #   skip other segments
  82 
  83 # - To pack an instruction, following the C++ version:
  84 # read line
  85 # parse words
  86 # read first word as opcode and write-slice
  87 # if 0f or f2 or f3 read second opcode and write-slice
  88 # if 'f2 0f' or 'f3 0f' read third opcode and write-slice
  89 # while true:
  90 #   word-slice = next-word
  91 #   if empty(word-slice) break
  92 #   if has metadata 'mod', parse into mod
  93 #   if has metadata 'rm32', parse into rm32
  94 #   if has metadata 'r32', parse into r32
  95 #   if has metadata 'subop', parse into r32
  96 # if at least one of the 3 was present, print-byte
  97 # while true:
  98 #   word-slice = next-word
  99 #   if empty(word-slice) break
 100 #   if has metadata 'base', parse into base
 101 #   if has metadata 'index', parse into index
 102 #   if has metadata 'scale', parse into scale
 103 # if at least one of the 3 was present, print-byte
 104 # parse errors => <abort>
 105 # while true:
 106 #   word-slice = next-word
 107 #   if empty(word-slice) break
 108 #   if has metadata 'disp8', emit as 1 byte
 109 #   if has metadata 'disp16', emit as 2 bytes
 110 #   if has metadata 'disp32', emit as 4 bytes
 111 # while true:
 112 #   word-slice = next-word
 113 #   if empty(word-slice) break
 114 #   if has metadata 'imm8', emit
 115 #   if has metadata 'imm32', emit as 4 bytes
 116 # finally, emit line prefixed with a '  # '
 117 
 118 # simplifications since we perform zero error handling (continuing to rely on the C++ version for that):
 119 #   missing fields are always 0-filled
 120 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 modrm byte. You get *no* modrm byte.
 121 #   in case of conflict, last operand with a name is recognized
 122 #   silently drop extraneous operands
 123 #   unceremoniously abort on non-numeric operands except disp or imm
 124 
 125 # primary state: line
 126 #   stream of 512 bytes; abort if it ever overflows
 127 #
 128 # conceptual hierarchy within a line:
 129 #   line = words separated by ' ', maybe followed by comment starting with '#'
 130 #   word = name until '/', then 0 or more metadata separated by '/'
 131 #
 132 # we won't bother saving the internal structure of lines; reparsing should be cheap using two primitives:
 133 #   next-token(stream, delim char) -> slice (start, end pointers)
 134 #   next-token(stream, slice, delim char) -> slice'
 135 #   slice-equal?(slice, string)
 136 
 137 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
 138     # pseudocode:
 139     #   line = new-stream(512, 1)
 140     #   repeatedly
 141     #     clear-stream(line)
 142     #     EAX = read-line(in, line, err, ed)
 143     #     if EAX == EOF break
 144     #     convert-instruction(line, out, err, ed)
 145     #   flush(out)
 146     #
 147     # . prolog
 148     55/push-EBP
 149     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 150     # . save registers
 151     # . restore registers
 152     # . epilog
 153     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 154     5d/pop-to-EBP
 155     c3/return
 156 
 157 # (re)compute the bounds of the next word in the line
 158 next-word:  # line : (address stream byte), out : (address slice)
 159     # . prolog
 160     55/push-EBP
 161     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 162     # . save registers
 163     50/push-EAX
 164     51/push-ECX
 165     56/push-ESI
 166     57/push-EDI
 167     # ESI = line
 168     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 169     # EDI = out
 170     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
 171     # skip-chars-matching(line, ' ')
 172     # . . push args
 173     68/push  0x20/imm32/space
 174     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 175     # . . call
 176     e8/call  skip-chars-matching/disp32
 177     # . . discard args
 178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 179     # out->start = &line->data[line->read]
 180     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 181     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
 182     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
 183     # if line->data[line->read] == '#': out->end = &line->data[line->write]), skip rest of stream and return
 184     # . EAX = line->data[line->read]
 185     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 186     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
 187     # . compare
 188     3d/compare-EAX-with  0x23/imm32/pound
 189     75/jump-if-not-equal  $next-word:not-comment/disp8
 190     # . out->end = &line->data[line->write]
 191     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
 192     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
 193     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 194     # . line->read = line->write
 195     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
 196     # . return
 197     eb/jump  $next-word:end/disp8
 198 $next-word:not-comment:
 199     # otherwise skip-chars-not-matching(line, ' ')
 200     # . . push args
 201     68/push  0x20/imm32/space
 202     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 203     # . . call
 204     e8/call  skip-chars-not-matching/disp32
 205     # . . discard args
 206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 207     # out->end = &line->data[line->read]
 208     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 209     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
 210     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 211 $next-word:end:
 212     # . restore registers
 213     5f/pop-to-EDI
 214     5e/pop-to-ESI
 215     59/pop-to-ECX
 216     58/pop-to-EAX
 217     # . epilog
 218     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 219     5d/pop-to-EBP
 220     c3/return
 221 
 222 test-next-word:
 223     # . prolog
 224     55/push-EBP
 225     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 226     # setup
 227     # . clear-stream(_test-stream)
 228     # . . push args
 229     68/push  _test-stream/imm32
 230     # . . call
 231     e8/call  clear-stream/disp32
 232     # . . discard args
 233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 234     # var slice/ECX = {0, 0}
 235     68/push  0/imm32/end
 236     68/push  0/imm32/start
 237     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 238     # write(_test-stream, "  ab")
 239     # . . push args
 240     68/push  "  ab"/imm32
 241     68/push  _test-stream/imm32
 242     # . . call
 243     e8/call  write/disp32
 244     # . . discard args
 245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 246     # next-word(_test-stream, slice)
 247     # . . push args
 248     51/push-ECX
 249     68/push  _test-stream/imm32
 250     # . . call
 251     e8/call  next-word/disp32
 252     # . . discard args
 253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 254     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
 255     # . check-ints-equal(slice->start - _test-stream, 14, msg)
 256     # . . push args
 257     68/push  "F - test-next-word: start"/imm32
 258     68/push  0xe/imm32
 259     # . . push slice->start - _test-stream
 260     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 261     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
 262     50/push-EAX
 263     # . . call
 264     e8/call  check-ints-equal/disp32
 265     # . . discard args
 266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 267     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
 268     # . check-ints-equal(slice->end - _test-stream, 16, msg)
 269     # . . push args
 270     68/push  "F - test-next-word: end"/imm32
 271     68/push  0x10/imm32
 272     # . . push slice->end - _test-stream
 273     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 274     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
 275     50/push-EAX
 276     # . . call
 277     e8/call  check-ints-equal/disp32
 278     # . . discard args
 279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 280     # . epilog
 281     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 282     5d/pop-to-EBP
 283     c3/return
 284 
 285 test-next-word-returns-whole-comment:
 286     # . prolog
 287     55/push-EBP
 288     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 289     # setup
 290     # . clear-stream(_test-stream)
 291     # . . push args
 292     68/push  _test-stream/imm32
 293     # . . call
 294     e8/call  clear-stream/disp32
 295     # . . discard args
 296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 297     # var slice/ECX = {0, 0}
 298     68/push  0/imm32/end
 299     68/push  0/imm32/start
 300     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 301     # write(_test-stream, "  # a")
 302     # . . push args
 303     68/push  "  # a"/imm32
 304     68/push  _test-stream/imm32
 305     # . . call
 306     e8/call  write/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 309     # next-word(_test-stream, slice)
 310     # . . push args
 311     51/push-ECX
 312     68/push  _test-stream/imm32
 313     # . . call
 314     e8/call  next-word/disp32
 315     # . . discard args
 316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 317     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
 318     # . check-ints-equal(slice->start - _test-stream, 14, msg)
 319     # . . push args
 320     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
 321     68/push  0xe/imm32
 322     # . . push slice->start - _test-stream
 323     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 324     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
 325     50/push-EAX
 326     # . . call
 327     e8/call  check-ints-equal/disp32
 328     # . . discard args
 329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 330     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
 331     # . check-ints-equal(slice->end - _test-stream, 17, msg)
 332     # . . push args
 333     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
 334     68/push  0x11/imm32
 335     # . . push slice->end - _test-stream
 336     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 337     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
 338     50/push-EAX
 339     # . . call
 340     e8/call  check-ints-equal/disp32
 341     # . . discard args
 342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 343     # . epilog
 344     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 345     5d/pop-to-EBP
 346     c3/return
 347 
 348 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
 349     # pseudocode:
 350     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
 351     #   curr = twig->end
 352     #   while true:
 353     #     twig = next-token-from-slice(curr, word->end, '/')
 354     #     if twig.empty() break
 355     #     if slice-equal?(twig, s) return true
 356     #     curr = twig->end
 357     #   return false
 358     # . prolog
 359     55/push-EBP
 360     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 361     # . save registers
 362     51/push-ECX
 363     52/push-EDX
 364     56/push-ESI
 365     57/push-EDI
 366     # ESI = word
 367     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 368     # EDX = word->end
 369     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
 370     # var twig/EDI : (address slice) = {0, 0}
 371     68/push  0/imm32/end
 372     68/push  0/imm32/start
 373     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
 374     # next-token-from-slice(word->start, word->end, '/', twig)
 375     # . . push args
 376     57/push-EDI
 377     68/push  0x2f/imm32/slash
 378     52/push-EDX
 379     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
 380     # . . call
 381     e8/call  next-token-from-slice/disp32
 382     # . . discard args
 383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 384     # curr/ECX = twig->end
 385     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 386 $has-metadata?:loop:
 387     # next-token-from-slice(curr, word->end, '/', twig)
 388     # . . push args
 389     57/push-EDI
 390     68/push  0x2f/imm32/slash
 391     52/push-EDX
 392     51/push-ECX
 393     # . . call
 394     e8/call  next-token-from-slice/disp32
 395     # . . discard args
 396     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 397     # if slice-empty?(twig) return false
 398     # . EAX = slice-empty?(twig)
 399     # . . push args
 400     57/push-EDI
 401     # . . call
 402     e8/call  slice-empty?/disp32
 403     # . . discard args
 404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 405     # . if (EAX != 0) return false
 406     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
 407     75/compare-if-not-equal  $has-metadata?:false/disp8
 408     # if slice-equal?(twig, s) return true
 409     # . EAX = slice-equal?(twig, s)
 410     # . . push args
 411     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 412     57/push-EDI
 413     # . . call
 414     e8/call  slice-equal?/disp32
 415     # . . discard args
 416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 417     # . if (EAX != 0) return true
 418     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
 419     75/compare-if-not-equal  $has-metadata?:true/disp8
 420     # curr = twig->end
 421     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 422     eb/jump  $has-metadata?:loop/disp8
 423 $has-metadata?:true:
 424     b8/copy-to-EAX  1/imm32/true
 425     eb/jump  $has-metadata?:end/disp8
 426 $has-metadata?:false:
 427     b8/copy-to-EAX  0/imm32/false
 428 $has-metadata?:end:
 429     # . reclaim locals
 430     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 431     # . restore registers
 432     5f/pop-to-EDI
 433     5e/pop-to-ESI
 434     5a/pop-to-EDX
 435     59/pop-to-ECX
 436     # . epilog
 437     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 438     5d/pop-to-EBP
 439     c3/return
 440 
 441 test-has-metadata-true:
 442     # . prolog
 443     55/push-EBP
 444     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 445     # (EAX..ECX) = "ab/c"
 446     b8/copy-to-EAX  "ab/c"/imm32
 447     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 448     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
 449     05/add-to-EAX  4/imm32
 450     # var in/ESI : (address slice) = {EAX, ECX}
 451     51/push-ECX
 452     50/push-EAX
 453     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 454     # EAX = has-metadata?(ESI, "c")
 455     # . . push args
 456     68/push  "c"/imm32
 457     56/push-ESI
 458     # . . call
 459     e8/call  has-metadata?/disp32
 460     # . . discard args
 461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 462     # check-ints-equal(EAX, 1, msg)
 463     # . . push args
 464     68/push  "F - test-has-metadata-true"/imm32
 465     68/push  1/imm32/true
 466     50/push-EAX
 467     # . . call
 468     e8/call  check-ints-equal/disp32
 469     # . . discard args
 470     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 471     # . epilog
 472     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 473     5d/pop-to-EBP
 474     c3/return
 475 
 476 test-has-metadata-false:
 477     # . prolog
 478     55/push-EBP
 479     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 480     # (EAX..ECX) = "ab/c"
 481     b8/copy-to-EAX  "ab/c"/imm32
 482     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 483     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
 484     05/add-to-EAX  4/imm32
 485     # var in/ESI : (address slice) = {EAX, ECX}
 486     51/push-ECX
 487     50/push-EAX
 488     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 489     # EAX = has-metadata?(ESI, "c")
 490     # . . push args
 491     68/push  "d"/imm32
 492     56/push-ESI
 493     # . . call
 494     e8/call  has-metadata?/disp32
 495     # . . discard args
 496     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 497     # check-ints-equal(EAX, 0, msg)
 498     # . . push args
 499     68/push  "F - test-has-metadata-false"/imm32
 500     68/push  0/imm32/false
 501     50/push-EAX
 502     # . . call
 503     e8/call  check-ints-equal/disp32
 504     # . . discard args
 505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 506     # . epilog
 507     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 508     5d/pop-to-EBP
 509     c3/return
 510 
 511 test-has-metadata-ignore-name:
 512     # . prolog
 513     55/push-EBP
 514     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 515     # (EAX..ECX) = "a/b"
 516     b8/copy-to-EAX  "a/b"/imm32
 517     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 518     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
 519     05/add-to-EAX  4/imm32
 520     # var in/ESI : (address slice) = {EAX, ECX}
 521     51/push-ECX
 522     50/push-EAX
 523     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 524     # EAX = has-metadata?(ESI, "a")
 525     # . . push args
 526     68/push  "a"/imm32
 527     56/push-ESI
 528     # . . call
 529     e8/call  has-metadata?/disp32
 530     # . . discard args
 531     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 532     # check-ints-equal(EAX, 0, msg)
 533     # . . push args
 534     68/push  "F - test-has-metadata-ignore-name"/imm32
 535     68/push  0/imm32/false
 536     50/push-EAX
 537     # . . call
 538     e8/call  check-ints-equal/disp32
 539     # . . discard args
 540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 541     # . epilog
 542     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 543     5d/pop-to-EBP
 544     c3/return
 545 
 546 test-has-metadata-multiple-true:
 547     # . prolog
 548     55/push-EBP
 549     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 550     # (EAX..ECX) = "a/b/c"
 551     b8/copy-to-EAX  "a/b/c"/imm32
 552     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 553     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
 554     05/add-to-EAX  4/imm32
 555     # var in/ESI : (address slice) = {EAX, ECX}
 556     51/push-ECX
 557     50/push-EAX
 558     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 559     # EAX = has-metadata?(ESI, "c")
 560     # . . push args
 561     68/push  "c"/imm32
 562     56/push-ESI
 563     # . . call
 564     e8/call  has-metadata?/disp32
 565     # . . discard args
 566     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 567     # check-ints-equal(EAX, 1, msg)
 568     # . . push args
 569     68/push  "F - test-has-metadata-multiple-true"/imm32
 570     68/push  1/imm32/true
 571     50/push-EAX
 572     # . . call
 573     e8/call  check-ints-equal/disp32
 574     # . . discard args
 575     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 576     # . epilog
 577     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 578     5d/pop-to-EBP
 579     c3/return
 580 
 581 test-has-metadata-multiple-false:
 582     # . prolog
 583     55/push-EBP
 584     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 585     # (EAX..ECX) = "a/b/c"
 586     b8/copy-to-EAX  "a/b/c"/imm32
 587     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 588     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
 589     05/add-to-EAX  4/imm32
 590     # var in/ESI : (address slice) = {EAX, ECX}
 591     51/push-ECX
 592     50/push-EAX
 593     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 594     # EAX = has-metadata?(ESI, "d")
 595     # . . push args
 596     68/push  "d"/imm32
 597     56/push-ESI
 598     # . . call
 599     e8/call  has-metadata?/disp32
 600     # . . discard args
 601     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 602     # check-ints-equal(EAX, 0, msg)
 603     # . . push args
 604     68/push  "F - test-has-metadata-multiple-false"/imm32
 605     68/push  0/imm32/false
 606     50/push-EAX
 607     # . . call
 608     e8/call  check-ints-equal/disp32
 609     # . . discard args
 610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 611     # . epilog
 612     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 613     5d/pop-to-EBP
 614     c3/return
 615 
 616 # if the name of 'word' is all hex digits, parse and emit it in 'width' bytes
 617 # of hex otherwise just write-slice
 618 emit:  # out : (address buffered-file), word : (address slice), width : int
 619     # . prolog
 620     55/push-EBP
 621     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 622     # . save registers
 623     50/push-EAX
 624     56/push-ESI
 625     57/push-EDI
 626     # ESI = word
 627     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
 628     # var name/EDI : (address slice) = {0, 0}
 629     68/push  0/imm32/end
 630     68/push  0/imm32/start
 631     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
 632     # name = next-token-from-slice(word->start, word->end, '/')
 633     # . . push args
 634     57/push-EDI
 635     68/push  0x2f/imm32/slash
 636     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
 637     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
 638     # . . call
 639     e8/call  next-token-from-slice/disp32
 640     # . . discard args
 641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 642     # if !is-hex-int?(name) write-slice(out, word) and return
 643     # . is-hex-int?(name)
 644     # . . push args
 645     57/push-EDI
 646     # . . call
 647     e8/call  is-hex-int?/disp32
 648     # . . discard args
 649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 650     # . if EAX == 0
 651     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
 652     75/jump-if-not-equal  $emit:hex-int/disp8
 653     # . write-slice(out, word)
 654     # . . push args
 655     56/push-ESI
 656     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 657     # . . call
 658     e8/call  write-slice/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 661     # . return
 662     eb/jump  $emit:end/disp8
 663     # otherwise emit-hex(out, parse-hex-int(name), width)
 664 $emit:hex-int:
 665     # . n/EAX = parse-hex-int(name)
 666     # . . push args
 667     57/push-EDI
 668     # . . call
 669     e8/call  parse-hex-int/disp32
 670     # . . discard args
 671     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 672     # . emit-hex(out, n, width)
 673     # . . push args
 674     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 675     50/push-EAX
 676     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 677     # . . call
 678     e8/call  emit-hex/disp32
 679     # . . discard args
 680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 681 $emit:end:
 682     # . reclaim locals
 683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 684     # . restore registers
 685     5f/pop-to-EDI
 686     5e/pop-to-ESI
 687     58/pop-to-EAX
 688     # . epilog
 689     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 690     5d/pop-to-EBP
 691     c3/return
 692 
 693 test-emit-number:
 694     # . prolog
 695     55/push-EBP
 696     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 697     # setup
 698     # . clear-stream(_test-stream)
 699     # . . push args
 700     68/push  _test-stream/imm32
 701     # . . call
 702     e8/call  clear-stream/disp32
 703     # . . discard args
 704     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 705     # . clear-stream(_test-buffered-file+4)
 706     # . . push args
 707     b8/copy-to-EAX  _test-buffered-file/imm32
 708     05/add-to-EAX  4/imm32
 709     50/push-EAX
 710     # . . call
 711     e8/call  clear-stream/disp32
 712     # . . discard args
 713     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 714     # var slice/ECX = "-2"
 715     68/push  _test-slice-negative-two-end/imm32/end
 716     68/push  _test-slice-negative-two/imm32/start
 717     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 718     # emit(_test-buffered-file, slice, 2)
 719     # . . push args
 720     68/push  2/imm32
 721     51/push-ECX
 722     68/push  _test-buffered-file/imm32
 723     # . . call
 724     e8/call  emit/disp32
 725     # . . discard args
 726     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 727     # flush(_test-buffered-file)
 728     # . . push args
 729     68/push  _test-buffered-file/imm32
 730     # . . call
 731     e8/call  flush/disp32
 732     # . . discard args
 733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 734     # check(_test-stream->data == 'fe ff ')
 735     # . check-ints-equal(_test-stream->data[0..3], 'fe f', msg)
 736     # . . push args
 737     68/push  "F - test-emit-number/1"/imm32
 738     68/push  0x66206566/imm32
 739     # . . push *_test-stream->data
 740     b8/copy-to-EAX  _test-stream/imm32
 741     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
 742     # . . call
 743     e8/call  check-ints-equal/disp32
 744     # . . discard args
 745     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 746     # . check-ints-equal(_test-stream->data[4..7], 'f ', msg)
 747     # . . push args
 748     68/push  "F - test-emit-number/2"/imm32
 749     68/push  0x2066/imm32
 750     # . . push *_test-stream->data
 751     b8/copy-to-EAX  _test-stream/imm32
 752     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
 753     # . . call
 754     e8/call  check-ints-equal/disp32
 755     # . . discard args
 756     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 757     # . epilog
 758     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 759     5d/pop-to-EBP
 760     c3/return
 761 
 762 test-emit-number-with-metadata:
 763     # . prolog
 764     55/push-EBP
 765     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 766     # setup
 767     # . clear-stream(_test-stream)
 768     # . . push args
 769     68/push  _test-stream/imm32
 770     # . . call
 771     e8/call  clear-stream/disp32
 772     # . . discard args
 773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 774     # . clear-stream(_test-buffered-file+4)
 775     # . . push args
 776     b8/copy-to-EAX  _test-buffered-file/imm32
 777     05/add-to-EAX  4/imm32
 778     50/push-EAX
 779     # . . call
 780     e8/call  clear-stream/disp32
 781     # . . discard args
 782     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 783     # var slice/ECX = "-2/foo"
 784     68/push  _test-slice-negative-two-metadata-end/imm32/end
 785     68/push  _test-slice-negative-two/imm32/start
 786     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 787     # emit(_test-buffered-file, slice, 2)
 788     # . . push args
 789     68/push  2/imm32
 790     51/push-ECX
 791     68/push  _test-buffered-file/imm32
 792     # . . call
 793     e8/call  emit/disp32
 794     # . . discard args
 795     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 796     # flush(_test-buffered-file)
 797     # . . push args
 798     68/push  _test-buffered-file/imm32
 799     # . . call
 800     e8/call  flush/disp32
 801     # . . discard args
 802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 803     # the '/foo' will have no impact on the output
 804     # check(_test-stream->data == 'fe ff ')
 805     # . check-ints-equal(_test-stream->data[0..3], 'fe f', msg)
 806     # . . push args
 807     68/push  "F - test-emit-number-with-metadata/1"/imm32
 808     68/push  0x66206566/imm32
 809     # . . push *_test-stream->data
 810     b8/copy-to-EAX  _test-stream/imm32
 811     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
 812     # . . call
 813     e8/call  check-ints-equal/disp32
 814     # . . discard args
 815     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 816     # . check-ints-equal(_test-stream->data[4..7], 'f ', msg)
 817     # . . push args
 818     68/push  "F - test-emit-number-with-metadata/2"/imm32
 819     68/push  0x2066/imm32
 820     # . . push *_test-stream->data
 821     b8/copy-to-EAX  _test-stream/imm32
 822     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
 823     # . . call
 824     e8/call  check-ints-equal/disp32
 825     # . . discard args
 826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 827     # . epilog
 828     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 829     5d/pop-to-EBP
 830     c3/return
 831 
 832 test-emit-non-number:
 833     # . prolog
 834     55/push-EBP
 835     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 836     # setup
 837     # . clear-stream(_test-stream)
 838     # . . push args
 839     68/push  _test-stream/imm32
 840     # . . call
 841     e8/call  clear-stream/disp32
 842     # . . discard args
 843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 844     # . clear-stream(_test-buffered-file+4)
 845     # . . push args
 846     b8/copy-to-EAX  _test-buffered-file/imm32
 847     05/add-to-EAX  4/imm32
 848     50/push-EAX
 849     # . . call
 850     e8/call  clear-stream/disp32
 851     # . . discard args
 852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 853     # var slice/ECX = "xyz"
 854     68/push  _test-slice-non-number-word-end/imm32/end
 855     68/push  _test-slice-non-number-word/imm32/start
 856     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 857     # emit(_test-buffered-file, slice, 2)
 858     # . . push args
 859     68/push  2/imm32
 860     51/push-ECX
 861     68/push  _test-buffered-file/imm32
 862     # . . call
 863     e8/call  emit/disp32
 864     # . . discard args
 865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 866     # flush(_test-buffered-file)
 867     # . . push args
 868     68/push  _test-buffered-file/imm32
 869     # . . call
 870     e8/call  flush/disp32
 871     # . . discard args
 872     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 873     # check(_test-stream->data == 'xyz')
 874     # . check-ints-equal(_test-stream->data[0..3], 'xyz', msg)
 875     # . . push args
 876     68/push  "F - test-emit-non-number"/imm32
 877     68/push  0x7a7978/imm32
 878     # . . push *_test-stream->data
 879     b8/copy-to-EAX  _test-stream/imm32
 880     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
 881     # . . call
 882     e8/call  check-ints-equal/disp32
 883     # . . discard args
 884     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 885     # . epilog
 886     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 887     5d/pop-to-EBP
 888     c3/return
 889 
 890 test-emit-non-number-with-metadata:
 891     # . prolog
 892     55/push-EBP
 893     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 894     # setup
 895     # . clear-stream(_test-stream)
 896     # . . push args
 897     68/push  _test-stream/imm32
 898     # . . call
 899     e8/call  clear-stream/disp32
 900     # . . discard args
 901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 902     # . clear-stream(_test-buffered-file+4)
 903     # . . push args
 904     b8/copy-to-EAX  _test-buffered-file/imm32
 905     05/add-to-EAX  4/imm32
 906     50/push-EAX
 907     # . . call
 908     e8/call  clear-stream/disp32
 909     # . . discard args
 910     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 911     # var slice/ECX = "xyz/"
 912     68/push  _test-slice-non-number-word-metadata-end/imm32/end
 913     68/push  _test-slice-non-number-word/imm32/start
 914     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 915     # emit(_test-buffered-file, slice, 2)
 916     # . . push args
 917     68/push  2/imm32
 918     51/push-ECX
 919     68/push  _test-buffered-file/imm32
 920     # . . call
 921     e8/call  emit/disp32
 922     # . . discard args
 923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 924     # flush(_test-buffered-file)
 925     # . . push args
 926     68/push  _test-buffered-file/imm32
 927     # . . call
 928     e8/call  flush/disp32
 929     # . . discard args
 930     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 931     # check(_test-stream->data == 'xyz/')
 932     # . check-ints-equal(_test-stream->data[0..3], 'xyz/', msg)
 933     # . . push args
 934     68/push  "F - test-emit-non-number-with-metadata"/imm32
 935     68/push  0x2f7a7978/imm32
 936     # . . push *_test-stream->data
 937     b8/copy-to-EAX  _test-stream/imm32
 938     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
 939     # . . call
 940     e8/call  check-ints-equal/disp32
 941     # . . discard args
 942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 943     # . epilog
 944     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 945     5d/pop-to-EBP
 946     c3/return
 947 
 948 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
 949 emit-hex:  # out : (address buffered-file), n : int, width : int
 950     # . prolog
 951     55/push-EBP
 952     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 953     # . save registers
 954     50/push-EAX
 955     51/push-ECX
 956     52/push-EDX
 957     53/push-EBX
 958     57/push-EDI
 959     # EDI = out
 960     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
 961     # EBX = n
 962     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
 963     # EDX = width
 964     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
 965     # var curr/ECX = 0
 966     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
 967 $emit-hex:loop:
 968     # if (curr >= width) break
 969     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
 970     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
 971     # print-byte(out, EBX)
 972     # . . push args
 973     53/push-EBX
 974     57/push-EDI
 975     # . . call
 976     e8/call  print-byte/disp32
 977     # . . discard args
 978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 979     # write-byte(out, ' ')
 980     # . . push args
 981     68/push  0x20/imm32/space
 982     57/push-EDI
 983     # . . call
 984     e8/call  write-byte/disp32
 985     # . . discard args
 986     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 987     # EBX = EBX >> 8
 988     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
 989     # ++curr
 990     41/increment-ECX
 991     eb/jump  $emit-hex:loop/disp8
 992 $emit-hex:end:
 993     # . restore registers
 994     5f/pop-to-EDI
 995     5b/pop-to-EBX
 996     5a/pop-to-EAX
 997     59/pop-to-ECX
 998     58/pop-to-EAX
 999     # . epilog
1000     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1001     5d/pop-to-EBP
1002     c3/return
1003 
1004 test-emit-hex-single-byte:
1005     # setup
1006     # . clear-stream(_test-stream)
1007     # . . push args
1008     68/push  _test-stream/imm32
1009     # . . call
1010     e8/call  clear-stream/disp32
1011     # . . discard args
1012     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1013     # . clear-stream(_test-buffered-file+4)
1014     # . . push args
1015     b8/copy-to-EAX  _test-buffered-file/imm32
1016     05/add-to-EAX  4/imm32
1017     50/push-EAX
1018     # . . call
1019     e8/call  clear-stream/disp32
1020     # . . discard args
1021     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1022     # emit-hex(_test-buffered-file, 0xab, 1)
1023     # . . push args
1024     68/push  1/imm32
1025     68/push  0xab/imm32
1026     68/push  _test-buffered-file/imm32
1027     # . . call
1028     e8/call  emit-hex/disp32
1029     # . . discard args
1030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1031     # flush(_test-buffered-file)
1032     # . . push args
1033     68/push  _test-buffered-file/imm32
1034     # . . call
1035     e8/call  flush/disp32
1036     # . . discard args
1037     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1038     # check-ints-equal(*_test-stream->data, 'ab ', msg)
1039     # . . push args
1040     68/push  "F - test-emit-hex-single-byte"/imm32
1041     68/push  0x206261/imm32
1042     # . . push *_test-stream->data
1043     b8/copy-to-EAX  _test-stream/imm32
1044     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
1045     # . . call
1046     e8/call  check-ints-equal/disp32
1047     # . . discard args
1048     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1049     # . end
1050     c3/return
1051 
1052 test-emit-hex-multiple-byte:
1053     # setup
1054     # . clear-stream(_test-stream)
1055     # . . push args
1056     68/push  _test-stream/imm32
1057     # . . call
1058     e8/call  clear-stream/disp32
1059     # . . discard args
1060     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1061     # . clear-stream(_test-buffered-file+4)
1062     # . . push args
1063     b8/copy-to-EAX  _test-buffered-file/imm32
1064     05/add-to-EAX  4/imm32
1065     50/push-EAX
1066     # . . call
1067     e8/call  clear-stream/disp32
1068     # . . discard args
1069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1070     # emit-hex(_test-buffered-file, 0x1234, 2)
1071     # . . push args
1072     68/push  2/imm32
1073     68/push  0x1234/imm32
1074     68/push  _test-buffered-file/imm32
1075     # . . call
1076     e8/call  emit-hex/disp32
1077     # . . discard args
1078     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1079     # flush(_test-buffered-file)
1080     # . . push args
1081     68/push  _test-buffered-file/imm32
1082     # . . call
1083     e8/call  flush/disp32
1084     # . . discard args
1085     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1086     # check(_test-stream->data == '34 12 ')
1087     # . check-ints-equal(_test-stream->data[0..3], '34 1', msg)
1088     # . . push args
1089     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
1090     68/push  0x31203433/imm32
1091     # . . push *_test-stream->data
1092     b8/copy-to-EAX  _test-stream/imm32
1093     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
1094     # . . call
1095     e8/call  check-ints-equal/disp32
1096     # . . discard args
1097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1098     # . check-ints-equal(_test-stream->data[4..7], '2 ', msg)
1099     # . . push args
1100     68/push  "F - test-emit-hex-multiple-byte/2"/imm32
1101     68/push  0x2032/imm32
1102     # . . push *_test-stream->data
1103     b8/copy-to-EAX  _test-stream/imm32
1104     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
1105     # . . call
1106     e8/call  check-ints-equal/disp32
1107     # . . discard args
1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1109     # . end
1110     c3/return
1111 
1112 test-emit-hex-zero-pad:
1113     # setup
1114     # . clear-stream(_test-stream)
1115     # . . push args
1116     68/push  _test-stream/imm32
1117     # . . call
1118     e8/call  clear-stream/disp32
1119     # . . discard args
1120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1121     # . clear-stream(_test-buffered-file+4)
1122     # . . push args
1123     b8/copy-to-EAX  _test-buffered-file/imm32
1124     05/add-to-EAX  4/imm32
1125     50/push-EAX
1126     # . . call
1127     e8/call  clear-stream/disp32
1128     # . . discard args
1129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1130     # emit-hex(_test-buffered-file, 0xab, 2)
1131     # . . push args
1132     68/push  2/imm32
1133     68/push  0xab/imm32
1134     68/push  _test-buffered-file/imm32
1135     # . . call
1136     e8/call  emit-hex/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1139     # flush(_test-buffered-file)
1140     # . . push args
1141     68/push  _test-buffered-file/imm32
1142     # . . call
1143     e8/call  flush/disp32
1144     # . . discard args
1145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1146     # check(_test-stream->data == '00 ab')
1147     # . check-ints-equal(*_test-stream->data, 'ab 0', msg)
1148     # . . push args
1149     68/push  "F - test-emit-hex-zero-pad/1"/imm32
1150     68/push  0x30206261/imm32
1151     # . . push *_test-stream->data
1152     b8/copy-to-EAX  _test-stream/imm32
1153     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
1154     # . . call
1155     e8/call  check-ints-equal/disp32
1156     # . . discard args
1157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1158     # . check-ints-equal(*_test-stream->data[1], '0 ', msg)
1159     # . . push args
1160     68/push  "F - test-emit-hex-zero-pad/2"/imm32
1161     68/push  0x2030/imm32
1162     # . . push *_test-stream->data
1163     b8/copy-to-EAX  _test-stream/imm32
1164     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
1165     # . . call
1166     e8/call  check-ints-equal/disp32
1167     # . . discard args
1168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1169     # . end
1170     c3/return
1171 
1172 test-emit-hex-negative:
1173     # setup
1174     # . clear-stream(_test-stream)
1175     # . . push args
1176     68/push  _test-stream/imm32
1177     # . . call
1178     e8/call  clear-stream/disp32
1179     # . . discard args
1180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1181     # . clear-stream(_test-buffered-file+4)
1182     # . . push args
1183     b8/copy-to-EAX  _test-buffered-file/imm32
1184     05/add-to-EAX  4/imm32
1185     50/push-EAX
1186     # . . call
1187     e8/call  clear-stream/disp32
1188     # . . discard args
1189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1190     # emit-hex(_test-buffered-file, -1, 2)
1191     # . . push args
1192     68/push  2/imm32
1193     68/push  -1/imm32
1194     68/push  _test-buffered-file/imm32
1195     # . . call
1196     e8/call  emit-hex/disp32
1197     # . . discard args
1198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1199     # flush(_test-buffered-file)
1200     # . . push args
1201     68/push  _test-buffered-file/imm32
1202     # . . call
1203     e8/call  flush/disp32
1204     # . . discard args
1205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1206     # check(_test-stream->data == 'ff ff ')
1207     # . check-ints-equal(_test-stream->data[0..3], 'ff f', msg)
1208     # . . push args
1209     68/push  "F - test-emit-hex-negative/1"/imm32
1210     68/push  0x66206666/imm32
1211     # . . push *_test-stream->data
1212     b8/copy-to-EAX  _test-stream/imm32
1213     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
1214     # . . call
1215     e8/call  check-ints-equal/disp32
1216     # . . discard args
1217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1218     # . check-ints-equal(_test-stream->data[4..7], 'f ', msg)
1219     # . . push args
1220     68/push  "F - test-emit-hex-negative/2"/imm32
1221     68/push  0x2066/imm32
1222     # . . push *_test-stream->data
1223     b8/copy-to-EAX  _test-stream/imm32
1224     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
1225     # . . call
1226     e8/call  check-ints-equal/disp32
1227     # . . discard args
1228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1229     # . end
1230     c3/return
1231 
1232 == data
1233 
1234 _test-slice-negative-two:
1235     2d/- 32/2
1236 _test-slice-negative-two-end:
1237     2f/slash 66/f 6f/o 6f/o
1238 _test-slice-negative-two-metadata-end:
1239 
1240 _test-slice-non-number-word:
1241     78/x 79/y 7a/z
1242 _test-slice-non-number-word-end:
1243     2f/slash
1244 _test-slice-non-number-word-metadata-end:
1245 
1246 # . . vim:nowrap:textwidth=0