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