https://github.com/akkartik/mu/blob/master/apps/subx-common.subx
   1 # return shared by phases of the SubX translator
   2 
   3 == data
   4 
   5 # maximum memory available for allocation
   6 Heap-size:
   7   0x200000/imm32/2MB
   8 
   9 # maximum size of a single segment
  10 Segment-size:
  11   0x80000/imm32/512KB
  12 
  13 # maximum size of input textual stream (spanning all segments)
  14 Input-size:
  15   0x100000/imm32/1MB
  16 
  17 # maximum size of the 'labels' table in survey.subx
  18 Max-labels:
  19   0x10000/imm32/4K-labels/64KB
  20 
  21 == code
  22 #   instruction                     effective address                                                   register    displacement    immediate
  23 # . op          subop               mod             rm32          base        index         scale       r32
  24 # . 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
  25 
  26 # (re)compute the bounds of the next word in the line
  27 # return empty string on reaching end of file
  28 next-word:  # line : (address stream byte), out : (address slice)
  29     # . prolog
  30     55/push-ebp
  31     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  32     # . save registers
  33     50/push-eax
  34     51/push-ecx
  35     56/push-esi
  36     57/push-edi
  37     # esi = line
  38     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
  39     # edi = out
  40     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
  41     # skip-chars-matching(line, ' ')
  42     # . . push args
  43     68/push  0x20/imm32/space
  44     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  45     # . . call
  46     e8/call  skip-chars-matching/disp32
  47     # . . discard args
  48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  49 $next-word:check0:
  50     # if (line->read >= line->write) clear out and return
  51     # . eax = line->read
  52     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
  53     # . if (eax < line->write) goto next check
  54     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
  55     7c/jump-if-lesser  $next-word:check-for-comment/disp8
  56     # . return out = {0, 0}
  57     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
  58     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
  59     eb/jump  $next-word:end/disp8
  60 $next-word:check-for-comment:
  61     # out->start = &line->data[line->read]
  62     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
  63     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
  64     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
  65     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
  66     # . eax = line->data[line->read]
  67     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
  68     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
  69     # . compare
  70     3d/compare-eax-and  0x23/imm32/pound
  71     75/jump-if-not-equal  $next-word:regular-word/disp8
  72 $next-word:comment:
  73     # . out->end = &line->data[line->write]
  74     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
  75     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
  76     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
  77     # . line->read = line->write
  78     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
  79     # . return
  80     eb/jump  $next-word:end/disp8
  81 $next-word:regular-word:
  82     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
  83     # . . push args
  84     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  85     # . . call
  86     e8/call  skip-chars-not-matching-whitespace/disp32
  87     # . . discard args
  88     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  89     # out->end = &line->data[line->read]
  90     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
  91     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
  92     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
  93 $next-word:end:
  94     # . restore registers
  95     5f/pop-to-edi
  96     5e/pop-to-esi
  97     59/pop-to-ecx
  98     58/pop-to-eax
  99     # . epilog
 100     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 101     5d/pop-to-ebp
 102     c3/return
 103 
 104 test-next-word:
 105     # . prolog
 106     55/push-ebp
 107     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 108     # setup
 109     # . clear-stream(_test-stream)
 110     # . . push args
 111     68/push  _test-stream/imm32
 112     # . . call
 113     e8/call  clear-stream/disp32
 114     # . . discard args
 115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 116     # var slice/ecx = {0, 0}
 117     68/push  0/imm32/end
 118     68/push  0/imm32/start
 119     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 120     # write(_test-stream, "  ab")
 121     # . . push args
 122     68/push  "  ab"/imm32
 123     68/push  _test-stream/imm32
 124     # . . call
 125     e8/call  write/disp32
 126     # . . discard args
 127     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 128     # next-word(_test-stream, slice)
 129     # . . push args
 130     51/push-ecx
 131     68/push  _test-stream/imm32
 132     # . . call
 133     e8/call  next-word/disp32
 134     # . . discard args
 135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 136     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
 137     # . check-ints-equal(slice->start - _test-stream, 14, msg)
 138     # . . push args
 139     68/push  "F - test-next-word: start"/imm32
 140     68/push  0xe/imm32
 141     # . . push slice->start - _test-stream
 142     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 143     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
 144     50/push-eax
 145     # . . call
 146     e8/call  check-ints-equal/disp32
 147     # . . discard args
 148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 149     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
 150     # . check-ints-equal(slice->end - _test-stream, 16, msg)
 151     # . . push args
 152     68/push  "F - test-next-word: end"/imm32
 153     68/push  0x10/imm32
 154     # . . push slice->end - _test-stream
 155     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
 156     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
 157     50/push-eax
 158     # . . call
 159     e8/call  check-ints-equal/disp32
 160     # . . discard args
 161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 162     # . epilog
 163     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 164     5d/pop-to-ebp
 165     c3/return
 166 
 167 test-next-word-returns-whole-comment:
 168     # . prolog
 169     55/push-ebp
 170     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 171     # setup
 172     # . clear-stream(_test-stream)
 173     # . . push args
 174     68/push  _test-stream/imm32
 175     # . . call
 176     e8/call  clear-stream/disp32
 177     # . . discard args
 178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 179     # var slice/ecx = {0, 0}
 180     68/push  0/imm32/end
 181     68/push  0/imm32/start
 182     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 183     # write(_test-stream, "  # a")
 184     # . . push args
 185     68/push  "  # a"/imm32
 186     68/push  _test-stream/imm32
 187     # . . call
 188     e8/call  write/disp32
 189     # . . discard args
 190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 191     # next-word(_test-stream, slice)
 192     # . . push args
 193     51/push-ecx
 194     68/push  _test-stream/imm32
 195     # . . call
 196     e8/call  next-word/disp32
 197     # . . discard args
 198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 199     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
 200     # . check-ints-equal(slice->start - _test-stream, 14, msg)
 201     # . . push args
 202     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
 203     68/push  0xe/imm32
 204     # . . push slice->start - _test-stream
 205     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 206     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
 207     50/push-eax
 208     # . . call
 209     e8/call  check-ints-equal/disp32
 210     # . . discard args
 211     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 212     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
 213     # . check-ints-equal(slice->end - _test-stream, 17, msg)
 214     # . . push args
 215     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
 216     68/push  0x11/imm32
 217     # . . push slice->end - _test-stream
 218     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
 219     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
 220     50/push-eax
 221     # . . call
 222     e8/call  check-ints-equal/disp32
 223     # . . discard args
 224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 225     # . epilog
 226     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 227     5d/pop-to-ebp
 228     c3/return
 229 
 230 test-next-word-returns-empty-string-on-eof:
 231     # . prolog
 232     55/push-ebp
 233     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 234     # setup
 235     # . clear-stream(_test-stream)
 236     # . . push args
 237     68/push  _test-stream/imm32
 238     # . . call
 239     e8/call  clear-stream/disp32
 240     # . . discard args
 241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 242     # var slice/ecx = {0, 0}
 243     68/push  0/imm32/end
 244     68/push  0/imm32/start
 245     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 246     # write nothing to _test-stream
 247     # next-word(_test-stream, slice)
 248     # . . push args
 249     51/push-ecx
 250     68/push  _test-stream/imm32
 251     # . . call
 252     e8/call  next-word/disp32
 253     # . . discard args
 254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 255     # check-ints-equal(slice->end - slice->start, 0, msg)
 256     # . . push args
 257     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
 258     68/push  0/imm32
 259     # . . push slice->end - slice->start
 260     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
 261     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx 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     # . epilog
 268     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 269     5d/pop-to-ebp
 270     c3/return
 271 
 272 # write an entire stream's contents to a buffered-file
 273 # ways to do this:
 274 #   - construct a 'maximal slice' and pass it to write-slice-buffered
 275 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
 276 # we'll go with the first way for now
 277 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
 278     # . prolog
 279     55/push-ebp
 280     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 281     # . save registers
 282     50/push-eax
 283     51/push-ecx
 284     56/push-esi
 285     # esi = s
 286     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 287     # var slice/ecx = {s->data, s->data + s->write}
 288     # . push s->data + s->write
 289     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 290     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
 291     50/push-eax
 292     # . push s->data
 293     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
 294     50/push-eax
 295     # . ecx = esp
 296     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 297     # write-slice-buffered(f, slice)
 298     # . . push args
 299     51/push-ecx
 300     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 301     # . . call
 302     e8/call  write-slice-buffered/disp32
 303     # . . discard args
 304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 305 $write-stream-data:end:
 306     # . restore locals
 307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 308     # . restore registers
 309     5e/pop-to-esi
 310     59/pop-to-ecx
 311     58/pop-to-eax
 312     # . epilog
 313     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 314     5d/pop-to-ebp
 315     c3/return
 316 
 317 test-write-stream-data:
 318     # . prolog
 319     55/push-ebp
 320     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 321     # setup
 322     # . clear-stream(_test-output-stream)
 323     # . . push args
 324     68/push  _test-output-stream/imm32
 325     # . . call
 326     e8/call  clear-stream/disp32
 327     # . . discard args
 328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 329     # . clear-stream(_test-output-buffered-file+4)
 330     # . . push args
 331     b8/copy-to-eax  _test-output-buffered-file/imm32
 332     05/add-to-eax  4/imm32
 333     50/push-eax
 334     # . . call
 335     e8/call  clear-stream/disp32
 336     # . . discard args
 337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 338     # . clear-stream(_test-input-stream)
 339     # . . push args
 340     68/push  _test-input-stream/imm32
 341     # . . call
 342     e8/call  clear-stream/disp32
 343     # . . discard args
 344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 345     # initialize input
 346     # . write(_test-input-stream, "abcd")
 347     # . . push args
 348     68/push  "abcd"/imm32
 349     68/push  _test-input-stream/imm32
 350     # . . call
 351     e8/call  write/disp32
 352     # . . discard args
 353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 354     # write-stream-data(_test-output-buffered-file, _test-input-stream)
 355     # . . push args
 356     68/push  _test-input-stream/imm32
 357     68/push  _test-output-buffered-file/imm32
 358     # . . call
 359     e8/call  write-stream-data/disp32
 360     # . . discard args
 361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 362     # check that the write happened as expected
 363     # . flush(_test-output-buffered-file)
 364     # . . push args
 365     68/push  _test-output-buffered-file/imm32
 366     # . . call
 367     e8/call  flush/disp32
 368     # . . discard args
 369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 370     # . check-stream-equal(_test-output-stream, "abcd", msg)
 371     # . . push args
 372     68/push  "F - test-write-stream-data"/imm32
 373     68/push  "abcd"/imm32
 374     68/push  _test-output-stream/imm32
 375     # . . call
 376     e8/call  check-stream-equal/disp32
 377     # . . discard args
 378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 379     # . epilog
 380     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 381     5d/pop-to-ebp
 382     c3/return
 383 
 384 has-metadata?:  # word : (address slice), s : (address string) -> eax : boolean
 385     # pseudocode:
 386     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
 387     #   curr = twig->end
 388     #   while true
 389     #     twig = next-token-from-slice(curr, word->end, '/')
 390     #     if (twig.empty()) break
 391     #     if (slice-equal?(twig, s)) return true
 392     #     curr = twig->end
 393     #   return false
 394     # . prolog
 395     55/push-ebp
 396     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 397     # . save registers
 398     51/push-ecx
 399     52/push-edx
 400     56/push-esi
 401     57/push-edi
 402     # esi = word
 403     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 404     # edx = word->end
 405     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
 406     # var twig/edi : (address slice) = {0, 0}
 407     68/push  0/imm32/end
 408     68/push  0/imm32/start
 409     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 410     # next-token-from-slice(word->start, word->end, '/', twig)
 411     # . . push args
 412     57/push-edi
 413     68/push  0x2f/imm32/slash
 414     52/push-edx
 415     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 416     # . . call
 417     e8/call  next-token-from-slice/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 420     # curr/ecx = twig->end
 421     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 422 $has-metadata?:loop:
 423     # next-token-from-slice(curr, word->end, '/', twig)
 424     # . . push args
 425     57/push-edi
 426     68/push  0x2f/imm32/slash
 427     52/push-edx
 428     51/push-ecx
 429     # . . call
 430     e8/call  next-token-from-slice/disp32
 431     # . . discard args
 432     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 433     # if (slice-empty?(twig)) return false
 434     # . eax = slice-empty?(twig)
 435     # . . push args
 436     57/push-edi
 437     # . . call
 438     e8/call  slice-empty?/disp32
 439     # . . discard args
 440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 441     # . if (eax != 0) return false
 442     3d/compare-eax-and  0/imm32
 443     75/jump-if-not-equal  $has-metadata?:false/disp8
 444     # if (slice-equal?(twig, s)) return true
 445     # . eax = slice-equal?(twig, s)
 446     # . . push args
 447     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 448     57/push-edi
 449     # . . call
 450     e8/call  slice-equal?/disp32
 451     # . . discard args
 452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 453     # . if (eax != 0) return true
 454     3d/compare-eax-and  0/imm32
 455     75/jump-if-not-equal  $has-metadata?:true/disp8
 456     # curr = twig->end
 457     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 458     eb/jump  $has-metadata?:loop/disp8
 459 $has-metadata?:true:
 460     b8/copy-to-eax  1/imm32/true
 461     eb/jump  $has-metadata?:end/disp8
 462 $has-metadata?:false:
 463     b8/copy-to-eax  0/imm32/false
 464 $has-metadata?:end:
 465     # . reclaim locals
 466     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 467     # . restore registers
 468     5f/pop-to-edi
 469     5e/pop-to-esi
 470     5a/pop-to-edx
 471     59/pop-to-ecx
 472     # . epilog
 473     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 474     5d/pop-to-ebp
 475     c3/return
 476 
 477 test-has-metadata-true:
 478     # . prolog
 479     55/push-ebp
 480     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 481     # (eax..ecx) = "ab/imm32"
 482     b8/copy-to-eax  "ab/imm32"/imm32
 483     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 484     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
 485     05/add-to-eax  4/imm32
 486     # var in/esi : (address slice) = {eax, ecx}
 487     51/push-ecx
 488     50/push-eax
 489     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 490     # eax = has-metadata?(esi, "imm32")
 491     # . . push args
 492     68/push  "imm32"/imm32
 493     56/push-esi
 494     # . . call
 495     e8/call  has-metadata?/disp32
 496     # . . discard args
 497     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 498     # check-ints-equal(eax, 1, msg)
 499     # . . push args
 500     68/push  "F - test-has-metadata-true"/imm32
 501     68/push  1/imm32/true
 502     50/push-eax
 503     # . . call
 504     e8/call  check-ints-equal/disp32
 505     # . . discard args
 506     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 507     # . epilog
 508     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 509     5d/pop-to-ebp
 510     c3/return
 511 
 512 test-has-metadata-false:
 513     # . prolog
 514     55/push-ebp
 515     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 516     # (eax..ecx) = "ab/c"
 517     b8/copy-to-eax  "ab/c"/imm32
 518     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 519     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
 520     05/add-to-eax  4/imm32
 521     # var in/esi : (address slice) = {eax, ecx}
 522     51/push-ecx
 523     50/push-eax
 524     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 525     # eax = has-metadata?(esi, "d")
 526     # . . push args
 527     68/push  "d"/imm32
 528     56/push-esi
 529     # . . call
 530     e8/call  has-metadata?/disp32
 531     # . . discard args
 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 533     # check-ints-equal(eax, 0, msg)
 534     # . . push args
 535     68/push  "F - test-has-metadata-false"/imm32
 536     68/push  0/imm32/false
 537     50/push-eax
 538     # . . call
 539     e8/call  check-ints-equal/disp32
 540     # . . discard args
 541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 542     # . epilog
 543     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 544     5d/pop-to-ebp
 545     c3/return
 546 
 547 test-has-metadata-ignore-name:
 548     # . prolog
 549     55/push-ebp
 550     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 551     # (eax..ecx) = "a/b"
 552     b8/copy-to-eax  "a/b"/imm32
 553     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 554     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
 555     05/add-to-eax  4/imm32
 556     # var in/esi : (address slice) = {eax, ecx}
 557     51/push-ecx
 558     50/push-eax
 559     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 560     # eax = has-metadata?(esi, "a")
 561     # . . push args
 562     68/push  "a"/imm32
 563     56/push-esi
 564     # . . call
 565     e8/call  has-metadata?/disp32
 566     # . . discard args
 567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 568     # check-ints-equal(eax, 0, msg)
 569     # . . push args
 570     68/push  "F - test-has-metadata-ignore-name"/imm32
 571     68/push  0/imm32/false
 572     50/push-eax
 573     # . . call
 574     e8/call  check-ints-equal/disp32
 575     # . . discard args
 576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 577     # . epilog
 578     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 579     5d/pop-to-ebp
 580     c3/return
 581 
 582 test-has-metadata-multiple-true:
 583     # . prolog
 584     55/push-ebp
 585     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 586     # (eax..ecx) = "a/b/c"
 587     b8/copy-to-eax  "a/b/c"/imm32
 588     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 589     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
 590     05/add-to-eax  4/imm32
 591     # var in/esi : (address slice) = {eax, ecx}
 592     51/push-ecx
 593     50/push-eax
 594     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 595     # eax = has-metadata?(esi, "c")
 596     # . . push args
 597     68/push  "c"/imm32
 598     56/push-esi
 599     # . . call
 600     e8/call  has-metadata?/disp32
 601     # . . discard args
 602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 603     # check-ints-equal(eax, 1, msg)
 604     # . . push args
 605     68/push  "F - test-has-metadata-multiple-true"/imm32
 606     68/push  1/imm32/true
 607     50/push-eax
 608     # . . call
 609     e8/call  check-ints-equal/disp32
 610     # . . discard args
 611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 612     # . epilog
 613     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 614     5d/pop-to-ebp
 615     c3/return
 616 
 617 test-has-metadata-multiple-false:
 618     # . prolog
 619     55/push-ebp
 620     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 621     # (eax..ecx) = "a/b/c"
 622     b8/copy-to-eax  "a/b/c"/imm32
 623     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 624     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
 625     05/add-to-eax  4/imm32
 626     # var in/esi : (address slice) = {eax, ecx}
 627     51/push-ecx
 628     50/push-eax
 629     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 630     # eax = has-metadata?(esi, "d")
 631     # . . push args
 632     68/push  "d"/imm32
 633     56/push-esi
 634     # . . call
 635     e8/call  has-metadata?/disp32
 636     # . . discard args
 637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 638     # check-ints-equal(eax, 0, msg)
 639     # . . push args
 640     68/push  "F - test-has-metadata-multiple-false"/imm32
 641     68/push  0/imm32/false
 642     50/push-eax
 643     # . . call
 644     e8/call  check-ints-equal/disp32
 645     # . . discard args
 646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 647     # . epilog
 648     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 649     5d/pop-to-ebp
 650     c3/return
 651 
 652 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
 653 # it in 'width' bytes of hex, least significant first.
 654 # Otherwise just print the entire word including metadata.
 655 # Always print a trailing space.
 656 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
 657     # . prolog
 658     55/push-ebp
 659     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 660     # . save registers
 661     50/push-eax
 662     56/push-esi
 663     57/push-edi
 664     # esi = word
 665     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 666     # var name/edi : (address slice) = {0, 0}
 667     68/push  0/imm32/end
 668     68/push  0/imm32/start
 669     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 670     # datum = next-token-from-slice(word->start, word->end, '/')
 671     # . . push args
 672     57/push-edi
 673     68/push  0x2f/imm32/slash
 674     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
 675     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 676     # . . call
 677     e8/call  next-token-from-slice/disp32
 678     # . . discard args
 679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 680     # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
 681     # . eax = is-valid-name?(name)
 682     # . . push args
 683     57/push-edi
 684     # . . call
 685     e8/call  is-valid-name?/disp32
 686     # . . discard args
 687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 688     # . if (eax != 0)
 689     3d/compare-eax-and  0/imm32
 690     74/jump-if-equal  $emit:hex-int/disp8
 691 $emit:name:
 692     # . write-slice-buffered(out, word)
 693     # . . push args
 694     56/push-esi
 695     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 696     # . . call
 697     e8/call  write-slice-buffered/disp32
 698     # . . discard args
 699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 700     # . write-buffered(out, " ")
 701     # . . push args
 702     68/push  " "/imm32
 703     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 704     # . . call
 705     e8/call  write-buffered/disp32
 706     # . . discard args
 707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 708     # . return
 709     eb/jump  $emit:end/disp8
 710     # otherwise emit-hex(out, parse-hex-int(datum), width)
 711     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
 712     #   name or a hex number, but we're only going to be passing in real legal
 713     #   programs. We just want to make sure that valid names aren't treated as
 714     #   (valid) hex numbers.)
 715 $emit:hex-int:
 716     # . value/eax = parse-hex-int(datum)
 717     # . . push args
 718     57/push-edi
 719     # . . call
 720     e8/call  parse-hex-int/disp32
 721     # . . discard args
 722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 723     # . emit-hex(out, value, width)
 724     # . . push args
 725     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 726     50/push-eax
 727     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 728     # . . call
 729     e8/call  emit-hex/disp32
 730     # . . discard args
 731     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 732 $emit:end:
 733     # . reclaim locals
 734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 735     # . restore registers
 736     5f/pop-to-edi
 737     5e/pop-to-esi
 738     58/pop-to-eax
 739     # . epilog
 740     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 741     5d/pop-to-ebp
 742     c3/return
 743 
 744 test-emit-number:
 745     # . prolog
 746     55/push-ebp
 747     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 748     # setup
 749     # . clear-stream(_test-output-stream)
 750     # . . push args
 751     68/push  _test-output-stream/imm32
 752     # . . call
 753     e8/call  clear-stream/disp32
 754     # . . discard args
 755     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 756     # . clear-stream(_test-output-buffered-file+4)
 757     # . . push args
 758     b8/copy-to-eax  _test-output-buffered-file/imm32
 759     05/add-to-eax  4/imm32
 760     50/push-eax
 761     # . . call
 762     e8/call  clear-stream/disp32
 763     # . . discard args
 764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 765     # (eax..ecx) = "30"
 766     b8/copy-to-eax  "30"/imm32
 767     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 768     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
 769     05/add-to-eax  4/imm32
 770     # var slice/ecx = {eax, ecx}
 771     51/push-ecx
 772     50/push-eax
 773     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 774     # emit(_test-output-buffered-file, slice, 1)
 775     # . . push args
 776     68/push  1/imm32
 777     51/push-ecx
 778     68/push  _test-output-buffered-file/imm32
 779     # . . call
 780     e8/call  emit/disp32
 781     # . . discard args
 782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 783     # flush(_test-output-buffered-file)
 784     # . . push args
 785     68/push  _test-output-buffered-file/imm32
 786     # . . call
 787     e8/call  flush/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 790     # check-stream-equal(_test-output-stream, "30 ", msg)
 791     # . . push args
 792     68/push  "F - test-emit-number/1"/imm32
 793     68/push  "30 "/imm32
 794     68/push  _test-output-stream/imm32
 795     # . . call
 796     e8/call  check-stream-equal/disp32
 797     # . . discard args
 798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 799     # . epilog
 800     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 801     5d/pop-to-ebp
 802     c3/return
 803 
 804 test-emit-negative-number:
 805     # test support for sign-extending negative numbers
 806     # . prolog
 807     55/push-ebp
 808     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 809     # setup
 810     # . clear-stream(_test-output-stream)
 811     # . . push args
 812     68/push  _test-output-stream/imm32
 813     # . . call
 814     e8/call  clear-stream/disp32
 815     # . . discard args
 816     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 817     # . clear-stream(_test-output-buffered-file+4)
 818     # . . push args
 819     b8/copy-to-eax  _test-output-buffered-file/imm32
 820     05/add-to-eax  4/imm32
 821     50/push-eax
 822     # . . call
 823     e8/call  clear-stream/disp32
 824     # . . discard args
 825     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 826     # (eax..ecx) = "-2"
 827     b8/copy-to-eax  "-2"/imm32
 828     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 829     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
 830     05/add-to-eax  4/imm32
 831     # var slice/ecx = {eax, ecx}
 832     51/push-ecx
 833     50/push-eax
 834     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 835     # emit(_test-output-buffered-file, slice, 2)
 836     # . . push args
 837     68/push  2/imm32
 838     51/push-ecx
 839     68/push  _test-output-buffered-file/imm32
 840     # . . call
 841     e8/call  emit/disp32
 842     # . . discard args
 843     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 844     # flush(_test-output-buffered-file)
 845     # . . push args
 846     68/push  _test-output-buffered-file/imm32
 847     # . . call
 848     e8/call  flush/disp32
 849     # . . discard args
 850     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 851     # check-stream-equal(_test-output-stream, "fe ff ", msg)
 852     # . . push args
 853     68/push  "F - test-emit-number/1"/imm32
 854     68/push  "fe ff "/imm32
 855     68/push  _test-output-stream/imm32
 856     # . . call
 857     e8/call  check-stream-equal/disp32
 858     # . . discard args
 859     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 860     # . epilog
 861     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 862     5d/pop-to-ebp
 863     c3/return
 864 
 865 test-emit-number-with-metadata:
 866     # . prolog
 867     55/push-ebp
 868     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 869     # setup
 870     # . clear-stream(_test-output-stream)
 871     # . . push args
 872     68/push  _test-output-stream/imm32
 873     # . . call
 874     e8/call  clear-stream/disp32
 875     # . . discard args
 876     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 877     # . clear-stream(_test-output-buffered-file+4)
 878     # . . push args
 879     b8/copy-to-eax  _test-output-buffered-file/imm32
 880     05/add-to-eax  4/imm32
 881     50/push-eax
 882     # . . call
 883     e8/call  clear-stream/disp32
 884     # . . discard args
 885     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 886     # (eax..ecx) = "-2/foo"
 887     b8/copy-to-eax  "-2/foo"/imm32
 888     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 889     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
 890     05/add-to-eax  4/imm32
 891     # var slice/ecx = {eax, ecx}
 892     51/push-ecx
 893     50/push-eax
 894     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 895     # emit(_test-output-buffered-file, slice, 2)
 896     # . . push args
 897     68/push  2/imm32
 898     51/push-ecx
 899     68/push  _test-output-buffered-file/imm32
 900     # . . call
 901     e8/call  emit/disp32
 902     # . . discard args
 903     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 904     # flush(_test-output-buffered-file)
 905     # . . push args
 906     68/push  _test-output-buffered-file/imm32
 907     # . . call
 908     e8/call  flush/disp32
 909     # . . discard args
 910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 911     # the '/foo' will have no impact on the output
 912     # check-stream-equal(_test-output-stream, "fe ff ", msg)
 913     # . . push args
 914     68/push  "F - test-emit-number-with-metadata"/imm32
 915     68/push  "fe ff "/imm32
 916     68/push  _test-output-stream/imm32
 917     # . . call
 918     e8/call  check-stream-equal/disp32
 919     # . . discard args
 920     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 921     # . epilog
 922     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 923     5d/pop-to-ebp
 924     c3/return
 925 
 926 test-emit-non-number:
 927     # . prolog
 928     55/push-ebp
 929     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 930     # setup
 931     # . clear-stream(_test-output-stream)
 932     # . . push args
 933     68/push  _test-output-stream/imm32
 934     # . . call
 935     e8/call  clear-stream/disp32
 936     # . . discard args
 937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 938     # . clear-stream(_test-output-buffered-file+4)
 939     # . . push args
 940     b8/copy-to-eax  _test-output-buffered-file/imm32
 941     05/add-to-eax  4/imm32
 942     50/push-eax
 943     # . . call
 944     e8/call  clear-stream/disp32
 945     # . . discard args
 946     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 947     # (eax..ecx) = "xyz"
 948     b8/copy-to-eax  "xyz"/imm32
 949     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 950     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
 951     05/add-to-eax  4/imm32
 952     # var slice/ecx = {eax, ecx}
 953     51/push-ecx
 954     50/push-eax
 955     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 956     # emit(_test-output-buffered-file, slice, 2)
 957     # . . push args
 958     68/push  2/imm32
 959     51/push-ecx
 960     68/push  _test-output-buffered-file/imm32
 961     # . . call
 962     e8/call  emit/disp32
 963     # . . discard args
 964     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 965     # flush(_test-output-buffered-file)
 966     # . . push args
 967     68/push  _test-output-buffered-file/imm32
 968     # . . call
 969     e8/call  flush/disp32
 970     # . . discard args
 971     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 972     # check-stream-equal(_test-output-stream, "xyz", msg)
 973     # . . push args
 974     68/push  "F - test-emit-non-number"/imm32
 975     68/push  "xyz "/imm32
 976     68/push  _test-output-stream/imm32
 977     # . . call
 978     e8/call  check-stream-equal/disp32
 979     # . . discard args
 980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 981     # . epilog
 982     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 983     5d/pop-to-ebp
 984     c3/return
 985 
 986 test-emit-non-number-with-metadata:
 987     # . prolog
 988     55/push-ebp
 989     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 990     # setup
 991     # . clear-stream(_test-output-stream)
 992     # . . push args
 993     68/push  _test-output-stream/imm32
 994     # . . call
 995     e8/call  clear-stream/disp32
 996     # . . discard args
 997     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 998     # . clear-stream(_test-output-buffered-file+4)
 999     # . . push args
1000     b8/copy-to-eax  _test-output-buffered-file/imm32
1001     05/add-to-eax  4/imm32
1002     50/push-eax
1003     # . . call
1004     e8/call  clear-stream/disp32
1005     # . . discard args
1006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1007     # (eax..ecx) = "xyz/"
1008     b8/copy-to-eax  "xyz/"/imm32
1009     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1010     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
1011     05/add-to-eax  4/imm32
1012     # var slice/ecx = {eax, ecx}
1013     51/push-ecx
1014     50/push-eax
1015     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1016     # emit(_test-output-buffered-file, slice, 2)
1017     # . . push args
1018     68/push  2/imm32
1019     51/push-ecx
1020     68/push  _test-output-buffered-file/imm32
1021     # . . call
1022     e8/call  emit/disp32
1023     # . . discard args
1024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1025     # flush(_test-output-buffered-file)
1026     # . . push args
1027     68/push  _test-output-buffered-file/imm32
1028     # . . call
1029     e8/call  flush/disp32
1030     # . . discard args
1031     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1032     # check-stream-equal(_test-output-stream, "xyz/", msg)
1033     # . . push args
1034     68/push  "F - test-emit-non-number-with-metadata"/imm32
1035     68/push  "xyz/ "/imm32
1036     68/push  _test-output-stream/imm32
1037     # . . call
1038     e8/call  check-stream-equal/disp32
1039     # . . discard args
1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1041     # . epilog
1042     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1043     5d/pop-to-ebp
1044     c3/return
1045 
1046 test-emit-non-number-with-all-hex-digits-and-metadata:
1047     # . prolog
1048     55/push-ebp
1049     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1050     # setup
1051     # . clear-stream(_test-output-stream)
1052     # . . push args
1053     68/push  _test-output-stream/imm32
1054     # . . call
1055     e8/call  clear-stream/disp32
1056     # . . discard args
1057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1058     # . clear-stream(_test-output-buffered-file+4)
1059     # . . push args
1060     b8/copy-to-eax  _test-output-buffered-file/imm32
1061     05/add-to-eax  4/imm32
1062     50/push-eax
1063     # . . call
1064     e8/call  clear-stream/disp32
1065     # . . discard args
1066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1067     # (eax..ecx) = "abcd/xyz"
1068     b8/copy-to-eax  "abcd/xyz"/imm32
1069     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1070     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
1071     05/add-to-eax  4/imm32
1072     # var slice/ecx = {eax, ecx}
1073     51/push-ecx
1074     50/push-eax
1075     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1076     # emit(_test-output-buffered-file, slice, 2)
1077     # . . push args
1078     68/push  2/imm32
1079     51/push-ecx
1080     68/push  _test-output-buffered-file/imm32
1081     # . . call
1082     e8/call  emit/disp32
1083     # . . discard args
1084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1085     # flush(_test-output-buffered-file)
1086     # . . push args
1087     68/push  _test-output-buffered-file/imm32
1088     # . . call
1089     e8/call  flush/disp32
1090     # . . discard args
1091     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1092 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1118     # check-stream-equal(_test-output-stream, "abcd/xyz")
1119     # . . push args
1120     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
1121     68/push  "abcd/xyz "/imm32
1122     68/push  _test-output-stream/imm32
1123     # . . call
1124     e8/call  check-stream-equal/disp32
1125     # . . discard args
1126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1127     # . epilog
1128     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1129     5d/pop-to-ebp
1130     c3/return
1131 
1132 # conditions for 'valid' names that are not at risk of looking like hex numbers
1133 # keep in sync with the rules in labels.cc
1134 #: - if it starts with a digit, it's treated as a number. If it can't be
1135 #:   parsed as hex it will raise an error.
1136 #: - if it starts with '-' it's treated as a number.
1137 #: - if it starts with '0x' it's treated as a number. (redundant)
1138 #: - if it's two characters long, it can't be a name. Either it's a hex
1139 #:   byte, or it raises an error.
1140 is-valid-name?:  # in : (address slice) -> eax : boolean
1141     # . prolog
1142     55/push-ebp
1143     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1144     # . save registers
1145     51/push-ecx
1146     56/push-esi
1147     # esi = in
1148     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1149     # start/ecx = in->start
1150     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
1151     # end/eax = in->end
1152     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
1153 $is-valid-name?:check0:
1154     # if (start >= end) return false
1155     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # compare ecx with eax
1156     73/jump-if-greater-or-equal-unsigned  $is-valid-name?:false/disp8
1157 $is-valid-name?:check1:
1158     # eax -= ecx
1159     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1160     # if (eax == 2) return false
1161     3d/compare-eax-and  2/imm32
1162     74/jump-if-equal  $is-valid-name?:false/disp8
1163 $is-valid-name?:check2:
1164     # c/eax = *ecx
1165     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1166     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1167     # if (c == "-") return false
1168     3d/compare-eax-and  2d/imm32/-
1169     74/jump-if-equal  $is-valid-name?:false/disp8
1170 $is-valid-name?:check3a:
1171     # if (c < "0") return true
1172     3d/compare-eax-with  30/imm32/0
1173     7c/jump-if-lesser  $is-valid-name?:true/disp8
1174 $is-valid-name?:check3b:
1175     # if (c > "9") return true
1176     3d/compare-eax-with  39/imm32/9
1177     7f/jump-if-greater  $is-valid-name?:true/disp8
1178 $is-valid-name?:false:
1179     # return false
1180     b8/copy-to-eax  0/imm32/false
1181     eb/jump  $is-valid-name?:end/disp8
1182 $is-valid-name?:true:
1183     # return true
1184     b8/copy-to-eax  1/imm32/true
1185 $is-valid-name?:end:
1186     # . restore registers
1187     5e/pop-to-esi
1188     59/pop-to-ecx
1189     # . epilog
1190     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1191     5d/pop-to-ebp
1192     c3/return
1193 
1194 test-is-valid-name-digit-prefix:
1195     # . prolog
1196     55/push-ebp
1197     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1198     # (eax..ecx) = "34"
1199     b8/copy-to-eax  "34"/imm32
1200     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1201     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
1202     05/add-to-eax  4/imm32
1203     # var slice/ecx = {eax, ecx}
1204     51/push-ecx
1205     50/push-eax
1206     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1207     # eax = is-valid-name?(slice)
1208     # . . push args
1209     51/push-ecx
1210     # . . call
1211     e8/call  is-valid-name?/disp32
1212     # . . discard args
1213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1214     # check-ints-equal(eax, 0, msg)
1215     # . . push args
1216     68/push  "F - test-is-valid-name-digit-prefix"/imm32
1217     68/push  0/imm32/false
1218     50/push-eax
1219     # . . call
1220     e8/call  check-ints-equal/disp32
1221     # . . discard args
1222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1223     # . epilog
1224     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1225     5d/pop-to-ebp
1226     c3/return
1227 
1228 test-is-valid-name-negative-prefix:
1229     # . prolog
1230     55/push-ebp
1231     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1232     # (eax..ecx) = "-0x34"
1233     b8/copy-to-eax  "-0x34"/imm32
1234     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1235     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
1236     05/add-to-eax  4/imm32
1237     # var slice/ecx = {eax, ecx}
1238     51/push-ecx
1239     50/push-eax
1240     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1241     # eax = is-valid-name?(slice)
1242     # . . push args
1243     51/push-ecx
1244     # . . call
1245     e8/call  is-valid-name?/disp32
1246     # . . discard args
1247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1248     # check-ints-equal(eax, 0, msg)
1249     # . . push args
1250     68/push  "F - test-is-valid-name-negative-prefix"/imm32
1251     68/push  0/imm32/false
1252     50/push-eax
1253     # . . call
1254     e8/call  check-ints-equal/disp32
1255     # . . discard args
1256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1257     # . epilog
1258     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1259     5d/pop-to-ebp
1260     c3/return
1261 
1262 test-is-valid-name-0x-prefix:
1263     # . prolog
1264     55/push-ebp
1265     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1266     # (eax..ecx) = "0x34"
1267     b8/copy-to-eax  "0x34"/imm32
1268     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1269     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
1270     05/add-to-eax  4/imm32
1271     # var slice/ecx = {eax, ecx}
1272     51/push-ecx
1273     50/push-eax
1274     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1275     # eax = is-valid-name?(slice)
1276     # . . push args
1277     51/push-ecx
1278     # . . call
1279     e8/call  is-valid-name?/disp32
1280     # . . discard args
1281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1282     # check-ints-equal(eax, 0, msg)
1283     # . . push args
1284     68/push  "F - test-is-valid-name-0x-prefix"/imm32
1285     68/push  0/imm32/false
1286     50/push-eax
1287     # . . call
1288     e8/call  check-ints-equal/disp32
1289     # . . discard args
1290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1291     # . epilog
1292     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1293     5d/pop-to-ebp
1294     c3/return
1295 
1296 test-is-valid-name-starts-with-pre-digit:
1297     # . prolog
1298     55/push-ebp
1299     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1300     # (eax..ecx) = "/03"
1301     b8/copy-to-eax  "/03"/imm32
1302     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1303     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
1304     05/add-to-eax  4/imm32
1305     # var slice/ecx = {eax, ecx}
1306     51/push-ecx
1307     50/push-eax
1308     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1309     # eax = is-valid-name?(slice)
1310     # . . push args
1311     51/push-ecx
1312     # . . call
1313     e8/call  is-valid-name?/disp32
1314     # . . discard args
1315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1316     # check-ints-equal(eax, 1, msg)
1317     # . . push args
1318     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
1319     68/push  1/imm32/true
1320     50/push-eax
1321     # . . call
1322     e8/call  check-ints-equal/disp32
1323     # . . discard args
1324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1325     # . epilog
1326     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1327     5d/pop-to-ebp
1328     c3/return
1329 
1330 test-is-valid-name-starts-with-post-digit:
1331     # . prolog
1332     55/push-ebp
1333     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1334     # (eax..ecx) = "q34"
1335     b8/copy-to-eax  "q34"/imm32
1336     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1337     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
1338     05/add-to-eax  4/imm32
1339     # var slice/ecx = {eax, ecx}
1340     51/push-ecx
1341     50/push-eax
1342     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1343     # eax = is-valid-name?(slice)
1344     # . . push args
1345     51/push-ecx
1346     # . . call
1347     e8/call  is-valid-name?/disp32
1348     # . . discard args
1349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1350     # check-ints-equal(eax, 1, msg)
1351     # . . push args
1352     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
1353     68/push  1/imm32/true
1354     50/push-eax
1355     # . . call
1356     e8/call  check-ints-equal/disp32
1357     # . . discard args
1358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1359     # . epilog
1360     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1361     5d/pop-to-ebp
1362     c3/return
1363 
1364 test-is-valid-name-starts-with-digit:
1365     # . prolog
1366     55/push-ebp
1367     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1368     # (eax..ecx) = "0x34"
1369     b8/copy-to-eax  "0x34"/imm32
1370     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1371     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
1372     05/add-to-eax  4/imm32
1373     # var slice/ecx = {eax, ecx}
1374     51/push-ecx
1375     50/push-eax
1376     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1377     # eax = is-valid-name?(slice)
1378     # . . push args
1379     51/push-ecx
1380     # . . call
1381     e8/call  is-valid-name?/disp32
1382     # . . discard args
1383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1384     # check-ints-equal(eax, 0, msg)
1385     # . . push args
1386     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
1387     68/push  0/imm32/false
1388     50/push-eax
1389     # . . call
1390     e8/call  check-ints-equal/disp32
1391     # . . discard args
1392     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1393     # . epilog
1394     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1395     5d/pop-to-ebp
1396     c3/return
1397 
1398 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
1399 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
1400     # . prolog
1401     55/push-ebp
1402     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1403     # . save registers
1404     50/push-eax
1405     51/push-ecx
1406     52/push-edx
1407     53/push-ebx
1408     57/push-edi
1409     # edi = out
1410     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
1411     # ebx = n
1412     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(ebp+12) to ebx
1413     # edx = width
1414     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
1415     # var curr/ecx = 0
1416     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
1417 $emit-hex:loop:
1418     # if (curr >= width) break
1419     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1420     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
1421     # print-byte-buffered(out, ebx)
1422     # . . push args
1423     53/push-ebx
1424     57/push-edi
1425     # . . call
1426     e8/call  print-byte-buffered/disp32
1427     # . . discard args
1428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1429     # write-byte-buffered(out, ' ')
1430     # . . push args
1431     68/push  0x20/imm32/space
1432     57/push-edi
1433     # . . call
1434     e8/call  write-byte-buffered/disp32
1435     # . . discard args
1436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1437     # ebx = ebx >> 8
1438     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/ebx    .           .             .           .           .               8/imm8            # shift ebx right by 8 bits, while padding zeroes
1439 $emit-hex:continue:
1440     # ++curr
1441     41/increment-ecx
1442     eb/jump  $emit-hex:loop/disp8
1443 $emit-hex:end:
1444     # . restore registers
1445     5f/pop-to-edi
1446     5b/pop-to-ebx
1447     5a/pop-to-edx
1448     59/pop-to-ecx
1449     58/pop-to-eax
1450     # . epilog
1451     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1452     5d/pop-to-ebp
1453     c3/return
1454 
1455 test-emit-hex-single-byte:
1456     # setup
1457     # . clear-stream(_test-output-stream)
1458     # . . push args
1459     68/push  _test-output-stream/imm32
1460     # . . call
1461     e8/call  clear-stream/disp32
1462     # . . discard args
1463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1464     # . clear-stream(_test-output-buffered-file+4)
1465     # . . push args
1466     b8/copy-to-eax  _test-output-buffered-file/imm32
1467     05/add-to-eax  4/imm32
1468     50/push-eax
1469     # . . call
1470     e8/call  clear-stream/disp32
1471     # . . discard args
1472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1473     # emit-hex(_test-output-buffered-file, 0xab, 1)
1474     # . . push args
1475     68/push  1/imm32
1476     68/push  0xab/imm32
1477     68/push  _test-output-buffered-file/imm32
1478     # . . call
1479     e8/call  emit-hex/disp32
1480     # . . discard args
1481     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1482     # flush(_test-output-buffered-file)
1483     # . . push args
1484     68/push  _test-output-buffered-file/imm32
1485     # . . call
1486     e8/call  flush/disp32
1487     # . . discard args
1488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1489     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
1490     # . . push args
1491     68/push  "F - test-emit-hex-single-byte"/imm32
1492     68/push  0x206261/imm32
1493     # . . push *_test-output-stream->data
1494     b8/copy-to-eax  _test-output-stream/imm32
1495     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
1496     # . . call
1497     e8/call  check-ints-equal/disp32
1498     # . . discard args
1499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1500     # . end
1501     c3/return
1502 
1503 test-emit-hex-multiple-byte:
1504     # setup
1505     # . clear-stream(_test-output-stream)
1506     # . . push args
1507     68/push  _test-output-stream/imm32
1508     # . . call
1509     e8/call  clear-stream/disp32
1510     # . . discard args
1511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1512     # . clear-stream(_test-output-buffered-file+4)
1513     # . . push args
1514     b8/copy-to-eax  _test-output-buffered-file/imm32
1515     05/add-to-eax  4/imm32
1516     50/push-eax
1517     # . . call
1518     e8/call  clear-stream/disp32
1519     # . . discard args
1520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1521     # emit-hex(_test-output-buffered-file, 0x1234, 2)
1522     # . . push args
1523     68/push  2/imm32
1524     68/push  0x1234/imm32
1525     68/push  _test-output-buffered-file/imm32
1526     # . . call
1527     e8/call  emit-hex/disp32
1528     # . . discard args
1529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1530     # flush(_test-output-buffered-file)
1531     # . . push args
1532     68/push  _test-output-buffered-file/imm32
1533     # . . call
1534     e8/call  flush/disp32
1535     # . . discard args
1536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1537     # check-stream-equal(_test-output-stream, "34 12 ", msg)
1538     # . . push args
1539     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
1540     68/push  "34 12 "/imm32
1541     68/push  _test-output-stream/imm32
1542     # . . call
1543     e8/call  check-stream-equal/disp32
1544     # . . discard args
1545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1546     # . end
1547     c3/return
1548 
1549 test-emit-hex-zero-pad:
1550     # setup
1551     # . clear-stream(_test-output-stream)
1552     # . . push args
1553     68/push  _test-output-stream/imm32
1554     # . . call
1555     e8/call  clear-stream/disp32
1556     # . . discard args
1557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1558     # . clear-stream(_test-output-buffered-file+4)
1559     # . . push args
1560     b8/copy-to-eax  _test-output-buffered-file/imm32
1561     05/add-to-eax  4/imm32
1562     50/push-eax
1563     # . . call
1564     e8/call  clear-stream/disp32
1565     # . . discard args
1566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1567     # emit-hex(_test-output-buffered-file, 0xab, 2)
1568     # . . push args
1569     68/push  2/imm32
1570     68/push  0xab/imm32
1571     68/push  _test-output-buffered-file/imm32
1572     # . . call
1573     e8/call  emit-hex/disp32
1574     # . . discard args
1575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1576     # flush(_test-output-buffered-file)
1577     # . . push args
1578     68/push  _test-output-buffered-file/imm32
1579     # . . call
1580     e8/call  flush/disp32
1581     # . . discard args
1582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1583     # check(_test-output-stream->data == 'ab 00 ')
1584     # . . push args
1585     68/push  "F - test-emit-hex-zero-pad/1"/imm32
1586     68/push  "ab 00 "/imm32
1587     68/push  _test-output-stream/imm32
1588     # . . call
1589     e8/call  check-stream-equal/disp32
1590     # . . discard args
1591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1592     # . end
1593     c3/return
1594 
1595 test-emit-hex-negative:
1596     # setup
1597     # . clear-stream(_test-output-stream)
1598     # . . push args
1599     68/push  _test-output-stream/imm32
1600     # . . call
1601     e8/call  clear-stream/disp32
1602     # . . discard args
1603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1604     # . clear-stream(_test-output-buffered-file+4)
1605     # . . push args
1606     b8/copy-to-eax  _test-output-buffered-file/imm32
1607     05/add-to-eax  4/imm32
1608     50/push-eax
1609     # . . call
1610     e8/call  clear-stream/disp32
1611     # . . discard args
1612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1613     # emit-hex(_test-output-buffered-file, -1, 2)
1614     # . . push args
1615     68/push  2/imm32
1616     68/push  -1/imm32
1617     68/push  _test-output-buffered-file/imm32
1618     # . . call
1619     e8/call  emit-hex/disp32
1620     # . . discard args
1621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1622     # flush(_test-output-buffered-file)
1623     # . . push args
1624     68/push  _test-output-buffered-file/imm32
1625     # . . call
1626     e8/call  flush/disp32
1627     # . . discard args
1628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1629     # check-stream-equal(_test-output-stream == "ff ff ")
1630     # . . push args
1631     68/push  "F - test-emit-hex-negative/1"/imm32
1632     68/push  "ff ff "/imm32
1633     68/push  _test-output-stream/imm32
1634     # . . call
1635     e8/call  check-stream-equal/disp32
1636     # . . discard args
1637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1638     # . end
1639     c3/return
1640 
1641 # print 'arr' in hex with a space after every byte
1642 emit-hex-array:  # out : (address buffered-file), arr : (address array byte) -> <void>
1643     # . prolog
1644     55/push-ebp
1645     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1646     # . save registers
1647     50/push-eax
1648     51/push-ecx
1649     52/push-edx
1650     57/push-edi
1651     # edi = out
1652     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
1653     # edx = arr  # <== 0xbdffffe4
1654     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
1655     # curr/ecx = arr->data
1656     8d/copy-address                 1/mod/*+disp8   2/rm32/edx    .           .             .           1/r32/ecx   4/disp8         .                 # copy edx+4 to ecx
1657     # max/edx = arr->data + arr->length
1658     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
1659     01/add                          3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # add ecx to edx
1660     # eax = 0
1661     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1662 $emit-hex-array:loop:
1663     # if (curr >= width) break
1664     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1665     73/jump-if-greater-or-equal-unsigned  $emit-hex-array:end/disp8
1666     # emit-hex(out, *curr, width=1)
1667     # . . push args
1668     68/push  1/imm32/width
1669     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1670     50/push-eax
1671     57/push-edi
1672     # . . call
1673     e8/call  emit-hex/disp32
1674     # . . discard args
1675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1676     # ++curr
1677     41/increment-ecx
1678     eb/jump  $emit-hex-array:loop/disp8
1679 $emit-hex-array:end:
1680     # . restore registers
1681     5f/pop-to-edi
1682     5a/pop-to-edx
1683     59/pop-to-ecx
1684     58/pop-to-eax
1685     # . epilog
1686     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1687     5d/pop-to-ebp
1688     c3/return
1689 
1690 test-emit-hex-array:
1691     # . prolog
1692     55/push-ebp
1693     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1694     # setup
1695     # . clear-stream(_test-output-stream)
1696     # . . push args
1697     68/push  _test-output-stream/imm32
1698     # . . call
1699     e8/call  clear-stream/disp32
1700     # . . discard args
1701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1702     # . clear-stream(_test-output-buffered-file+4)
1703     # . . push args
1704     b8/copy-to-eax  _test-output-buffered-file/imm32
1705     05/add-to-eax  4/imm32
1706     50/push-eax
1707     # . . call
1708     e8/call  clear-stream/disp32
1709     # . . discard args
1710     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1711     # var arr/ecx (address array byte) = [01, 02, 03]
1712     68/push  0x00030201/imm32  # bytes 01 02 03
1713     68/push  3/imm32/length
1714     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1715     # emit-hex-array(_test-output-buffered-file, arr)
1716     # . . push args
1717     51/push-ecx
1718     68/push  _test-output-buffered-file/imm32
1719     # . . call
1720     e8/call  emit-hex-array/disp32
1721     # . . discard args
1722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1723     # . flush(_test-output-buffered-file)
1724     # . . push args
1725     68/push  _test-output-buffered-file/imm32
1726     # . . call
1727     e8/call  flush/disp32
1728     # . . discard args
1729     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1730 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1763     # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg)
1764     # . . push args
1765     68/push  "F - test-emit-hex-array"/imm32
1766     68/push  "01 02 03 "/imm32
1767     68/push  _test-output-stream/imm32
1768     # . . call
1769     e8/call  check-next-stream-line-equal/disp32
1770     # . . discard args
1771     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1772     # . epilog
1773     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1774     5d/pop-to-ebp
1775     c3/return
1776 
1777 compute-width: # word : (address array byte) -> eax : int
1778     # . prolog
1779     55/push-ebp
1780     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1781     # . save registers
1782     51/push-ecx
1783     # eax = word
1784     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to ecx
1785     # ecx = word + word->length
1786     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1787     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
1788     # eax = word->data
1789     05/add-to-eax  4/imm32
1790     # var in/ecx : (address slice) = {eax, ecx}
1791     51/push-ecx
1792     50/push-eax
1793     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1794     # return compute-width-of-slice(ecx)
1795     # . . push args
1796     51/push-ecx
1797     # . . call
1798     e8/call  compute-width-of-slice/disp32
1799     # . . discard args
1800     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1801 $compute-width:end:
1802     # . reclaim locals
1803     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1804     # . restore registers
1805     59/pop-to-ecx
1806     # . epilog
1807     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1808     5d/pop-to-ebp
1809     c3/return
1810 
1811 compute-width-of-slice: # s : (address slice) -> eax : int
1812     # . prolog
1813     55/push-ebp
1814     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1815     # . save registers
1816     51/push-ecx
1817     # ecx = s
1818     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1819     # if (has-metadata?(word, "imm32")) return 4
1820     # . eax = has-metadata?(word, "imm32")
1821     # . . push args
1822     68/push  "imm32"/imm32
1823     51/push-ecx
1824     # . . call
1825     e8/call  has-metadata?/disp32
1826     # . . discard args
1827     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1828     # . if (eax != 0) return 4
1829     3d/compare-eax-and  0/imm32
1830     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
1831     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
1832     # if (has-metadata?(word, "disp32")) return 4
1833     # . eax = has-metadata?(word, "disp32")
1834     # . . push args
1835     68/push  "disp32"/imm32
1836     51/push-ecx
1837     # . . call
1838     e8/call  has-metadata?/disp32
1839     # . . discard args
1840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1841     # . if (eax != 0) return 4
1842     3d/compare-eax-and  0/imm32
1843     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
1844     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
1845     # if (has-metadata?(word, "imm16")) return 2
1846     # . eax = has-metadata?(word, "imm16")
1847     # . . push args
1848     68/push  "imm16"/imm32
1849     51/push-ecx
1850     # . . call
1851     e8/call  has-metadata?/disp32
1852     # . . discard args
1853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1854     # . if (eax != 0) return 2
1855     3d/compare-eax-and  0/imm32
1856     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
1857     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
1858     # if (has-metadata?(word, "disp16")) return 2
1859     # . eax = has-metadata?(word, "disp16")
1860     # . . push args
1861     68/push  "disp16"/imm32
1862     51/push-ecx
1863     # . . call
1864     e8/call  has-metadata?/disp32
1865     # . . discard args
1866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1867     # . if (eax != 0) return 2
1868     3d/compare-eax-and  0/imm32
1869     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
1870     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
1871     # otherwise return 1
1872     b8/copy-to-eax  1/imm32
1873 $compute-width-of-slice:end:
1874     # . restore registers
1875     59/pop-to-ecx
1876     # . epilog
1877     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1878     5d/pop-to-ebp
1879     c3/return
1880 
1881 test-compute-width:
1882     # . prolog
1883     55/push-ebp
1884     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1885 $test-compute-width:imm8:
1886     # eax = compute-width("0x2/imm8")
1887     # . . push args
1888     68/push  "0x2/imm8"/imm32
1889     # . . call
1890     e8/call  compute-width/disp32
1891     # . . discard args
1892     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1893     # check-ints-equal(eax, 1, msg)
1894     # . . push args
1895     68/push  "F - test-compute-width: 0x2/imm8"/imm32
1896     50/push-eax
1897     68/push  1/imm32
1898     # . . call
1899     e8/call  check-ints-equal/disp32
1900     # . . discard args
1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1902 $test-compute-width:imm16:
1903     # eax = compute-width("4/imm16")
1904     # . . push args
1905     68/push  "4/imm16"/imm32
1906     # . . call
1907     e8/call  compute-width/disp32
1908     # . . discard args
1909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1910     # check-ints-equal(eax, 2, msg)
1911     # . . push args
1912     68/push  "F - test-compute-width: 4/imm16"/imm32
1913     50/push-eax
1914     68/push  2/imm32
1915     # . . call
1916     e8/call  check-ints-equal/disp32
1917     # . . discard args
1918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1919 $test-compute-width:imm32:
1920     # eax = compute-width("4/imm32")
1921     # . . push args
1922     68/push  "4/imm32"/imm32
1923     # . . call
1924     e8/call  compute-width/disp32
1925     # . . discard args
1926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1927     # check-ints-equal(eax, 4, msg)
1928     # . . push args
1929     68/push  "F - test-compute-width: 4/imm32"/imm32
1930     50/push-eax
1931     68/push  4/imm32
1932     # . . call
1933     e8/call  check-ints-equal/disp32
1934     # . . discard args
1935     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1936 $test-compute-width:disp8:
1937     # eax = compute-width("foo/disp8")
1938     # . . push args
1939     68/push  "foo/disp8"/imm32
1940     # . . call
1941     e8/call  compute-width/disp32
1942     # . . discard args
1943     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1944     # check-ints-equal(eax, 1, msg)
1945     # . . push args
1946     68/push  "F - test-compute-width: foo/disp8"/imm32
1947     50/push-eax
1948     68/push  1/imm32
1949     # . . call
1950     e8/call  check-ints-equal/disp32
1951     # . . discard args
1952     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1953 $test-compute-width:disp16:
1954     # eax = compute-width("foo/disp16")
1955     # . . push args
1956     68/push  "foo/disp16"/imm32
1957     # . . call
1958     e8/call  compute-width/disp32
1959     # . . discard args
1960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1961     # check-ints-equal(eax, 2, msg)
1962     # . . push args
1963     68/push  "F - test-compute-width: foo/disp16"/imm32
1964     50/push-eax
1965     68/push  2/imm32
1966     # . . call
1967     e8/call  check-ints-equal/disp32
1968     # . . discard args
1969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1970 $test-compute-width:disp32:
1971     # eax = compute-width("foo/disp32")
1972     # . . push args
1973     68/push  "foo/disp32"/imm32
1974     # . . call
1975     e8/call  compute-width/disp32
1976     # . . discard args
1977     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1978     # check-ints-equal(eax, 4, msg)
1979     # . . push args
1980     68/push  "F - test-compute-width: foo/disp32"/imm32
1981     50/push-eax
1982     68/push  4/imm32
1983     # . . call
1984     e8/call  check-ints-equal/disp32
1985     # . . discard args
1986     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1987 $test-compute-width:no-metadata:
1988     # eax = compute-width("45")
1989     # . . push args
1990     68/push  "45"/imm32
1991     # . . call
1992     e8/call  compute-width/disp32
1993     # . . discard args
1994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1995     # check-ints-equal(eax, 1, msg)
1996     # . . push args
1997     68/push  "F - test-compute-width: 45 (no metadata)"/imm32
1998     50/push-eax
1999     68/push  1/imm32
2000     # . . call
2001     e8/call  check-ints-equal/disp32
2002     # . . discard args
2003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2004     # . epilog
2005     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2006     5d/pop-to-ebp
2007     c3/return
2008 
2009 is-label?: # word : (address slice) -> eax : boolean
2010     # . prolog
2011     55/push-ebp
2012     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2013     # . save registers
2014     51/push-ecx
2015     # ecx = word
2016     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
2017     # ecx = word->end
2018     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(ecx+4) to ecx
2019     # return *(word->end - 1) == ':'
2020     # . eax = 0
2021     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2022     # . eax = *((char *) word->end - 1)
2023     8a/copy-byte                    1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/AL    -1/disp8         .                 # copy byte at *(ecx-1) to AL
2024     # . return (eax == ':')
2025     3d/compare-eax-and  0x3a/imm32/colon
2026     b8/copy-to-eax  1/imm32/true
2027     74/jump-if-equal  $is-label?:end/disp8
2028     b8/copy-to-eax  0/imm32/false
2029 $is-label?:end:
2030     # . restore registers
2031     59/pop-to-ecx
2032     # . epilog
2033     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2034     5d/pop-to-ebp
2035     c3/return
2036 
2037 test-is-label?:
2038     # . prolog
2039     55/push-ebp
2040     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2041 $test-is-label?:true:
2042     # (eax..ecx) = "AAA:"
2043     b8/copy-to-eax  "AAA:"/imm32
2044     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2045     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
2046     05/add-to-eax  4/imm32
2047     # var slice/ecx = {eax, ecx}
2048     51/push-ecx
2049     50/push-eax
2050     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2051     # is-label?(slice/ecx)
2052     # . . push args
2053     51/push-ecx
2054     # . . call
2055     e8/call  is-label?/disp32
2056     # . . discard args
2057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2058     # check-ints-equal(eax, 1, msg)
2059     # . . push args
2060     68/push  "F - test-is-label?:true"/imm32
2061     68/push  1/imm32
2062     50/push-eax
2063     # . . call
2064     e8/call  check-ints-equal/disp32
2065     # . . discard args
2066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2067 $test-is-label?:false:
2068     # (eax..ecx) = "AAA"
2069     b8/copy-to-eax  "AAA"/imm32
2070     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2071     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
2072     05/add-to-eax  4/imm32
2073     # var slice/ecx = {eax, ecx}
2074     51/push-ecx
2075     50/push-eax
2076     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2077     # is-label?(slice/ecx)
2078     # . . push args
2079     51/push-ecx
2080     # . . call
2081     e8/call  is-label?/disp32
2082     # . . discard args
2083     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2084     # check-ints-equal(eax, 0, msg)
2085     # . . push args
2086     68/push  "F - test-is-label?:false"/imm32
2087     68/push  0/imm32
2088     50/push-eax
2089     # . . call
2090     e8/call  check-ints-equal/disp32
2091     # . . discard args
2092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2093     # . epilog
2094     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2095     5d/pop-to-ebp
2096     c3/return
2097 
2098 == data
2099 
2100 _test-data-segment:
2101   64/d 61/a 74/t 61/a
2102 _test-data-segment-end:
2103 
2104 # . . vim:nowrap:textwidth=0