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