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