https://github.com/akkartik/mu/blob/master/apps/dquotes.subx
   1 # Translate literal strings within double quotes.
   2 # Replace them with references to new variables in the data segment.
   3 #
   4 # To run (from the subx/ directory):
   5 #   $ ./subx translate *.subx apps/dquotes.subx -o apps/dquotes
   6 #   $ cat x
   7 #   == code
   8 #   ab "cd ef"/imm32
   9 #   $ cat x  |./subx run apps/dquotes
  10 #   == code
  11 #   ab __string1/imm32
  12 #   == data
  13 #   __string1:
  14 #     5/imm32
  15 #     0x63/c 0x64/d 0x20/  0x65/e 0x66/f
  16 
  17 == code
  18 #   instruction                     effective address                                                   register    displacement    immediate
  19 # . op          subop               mod             rm32          base        index         scale       r32
  20 # . 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
  21 
  22 Entry:
  23     # initialize heap
  24     # . Heap = new-segment(Heap-size)
  25     # . . push args
  26     68/push  Heap/imm32
  27     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  28     # . . call
  29     e8/call  new-segment/disp32
  30     # . . discard args
  31     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  32 
  33     # run tests if necessary, convert stdin if not
  34     # . prolog
  35     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  36     # - if argc > 1 and argv[1] == "test", then return run_tests()
  37     # . argc > 1
  38     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  39     7e/jump-if-lesser-or-equal  $run-main/disp8
  40     # . argv[1] == "test"
  41     # . . push args
  42     68/push  "test"/imm32
  43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  44     # . . call
  45     e8/call  kernel-string-equal?/disp32
  46     # . . discard args
  47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  48     # . check result
  49     3d/compare-EAX-and  1/imm32
  50     75/jump-if-not-equal  $run-main/disp8
  51     # . run-tests()
  52     e8/call  run-tests/disp32
  53     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  54     eb/jump  $main:end/disp8
  55 $run-main:
  56     # - otherwise convert stdin
  57     # var ed/EAX : exit-descriptor
  58     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  59     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  60     # configure ed to really exit()
  61     # . ed->target = 0
  62     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  63     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  64     # . . push args
  65     50/push-EAX/ed
  66     68/push  Stderr/imm32
  67     68/push  Stdout/imm32
  68     68/push  Stdin/imm32
  69     # . . call
  70     e8/call  convert/disp32
  71     # . . discard args
  72     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  73     # . syscall(exit, 0)
  74     bb/copy-to-EBX  0/imm32
  75 $main:end:
  76     b8/copy-to-EAX  1/imm32/exit
  77     cd/syscall  0x80/imm8
  78 
  79 # conceptual hierarchy within a line:
  80 #   line = words separated by ' ', maybe followed by comment starting with '#'
  81 #   word = datum until '/', then 0 or more metadata separated by '/'
  82 
  83 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  84     # pseudocode:
  85     #   var line = new-stream(512, 1)
  86     #   var new-data-segment = new-stream(Heap, Segment-size, 1)
  87     #   write(new-data-segment, "== data\n")
  88     #   while true
  89     #     clear-stream(line)
  90     #     read-line-buffered(in, line)
  91     #     if (line->write == 0) break               # end of file
  92     #     while true
  93     #       var word-slice = next-word-or-string(line)
  94     #       if slice-empty?(word-slice)             # end of line
  95     #         break
  96     #       if slice-starts-with?(word-slice, "#")  # comment
  97     #         continue
  98     #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
  99     #         process-string-literal(word-slice, out, new-data-segment)
 100     #       else
 101     #         write-slice-buffered(out, word-slice)
 102     #       write(out, " ")
 103     #     write(out, "\n\n")
 104     #   write-stream-data(out, new-data-segment)
 105     #   flush(out)
 106     #
 107     # . prolog
 108     55/push-EBP
 109     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 110     # . save registers
 111     50/push-EAX
 112     51/push-ECX
 113     52/push-EDX
 114     53/push-EBX
 115     56/push-ESI
 116     57/push-EDI
 117     # var line/ECX : (address stream byte) = stream(512)
 118     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 119     68/push  0x200/imm32/length
 120     68/push  0/imm32/read
 121     68/push  0/imm32/write
 122     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 123     # var word-slice/EDX = {0, 0}
 124     68/push  0/imm32/end
 125     68/push  0/imm32/start
 126     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 127     # new-data-segment/EDI = new-stream(Heap, Segment-size, 1)
 128     # . EAX = new-stream(Heap, Segment-size, 1)
 129     # . . push args
 130     68/push  1/imm32
 131     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 132     68/push  Heap/imm32
 133     # . . call
 134     e8/call  new-stream/disp32
 135     # . . discard args
 136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 137     # . EDI = EAX
 138     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
 139     # write(new-data-segment, "== data\n")
 140     # . . push args
 141     68/push  "== data\n"/imm32
 142     57/push-EDI
 143     # . . call
 144     e8/call  write/disp32
 145     # . . discard args
 146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 147 $convert:line-loop:
 148     # clear-stream(line)
 149     # . . push args
 150     51/push-ECX
 151     # . . call
 152     e8/call  clear-stream/disp32
 153     # . . discard args
 154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 155     # read-line-buffered(in, line)
 156     # . . push args
 157     51/push-ECX
 158     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 159     # . . call
 160     e8/call  read-line-buffered/disp32
 161     # . . discard args
 162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 163 $convert:check0:
 164     # if (line->write == 0) break
 165     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 166     0f 84/jump-if-equal  $convert:break/disp32
 167 $convert:word-loop:
 168     # next-word-or-string(line, word-slice)
 169     # . . push args
 170     52/push-EDX
 171     51/push-ECX
 172     # . . call
 173     e8/call  next-word-or-string/disp32
 174     # . . discard args
 175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 176 $convert:check1:
 177     # if (slice-empty?(word-slice)) break
 178     # . EAX = slice-empty?(word-slice)
 179     # . . push args
 180     52/push-EDX
 181     # . . call
 182     e8/call  slice-empty?/disp32
 183     # . . discard args
 184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 185     # . if (EAX != 0) break
 186     3d/compare-EAX-and  0/imm32
 187     0f 85/jump-if-not-equal  $convert:next-line/disp32
 188 $convert:check-for-comment:
 189     # if (slice-starts-with?(word-slice, "#")) continue
 190     # . start/ESI = word-slice->start
 191     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
 192     # . c/EAX = *start
 193     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 194     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
 195     # . if (EAX == '#') continue
 196     3d/compare-EAX-and  0x23/imm32/hash
 197     74/jump-if-equal  $convert:word-loop/disp8
 198 $convert:check-for-string-literal:
 199     3d/compare-EAX-and  0x22/imm32/dquote
 200     75/jump-if-not-equal  $convert:regular-word/disp8
 201 $convert:string-literal:
 202     # process-string-literal(word-slice, out, new-data-segment)
 203     # . . push args
 204     57/push-EDI
 205     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 206     52/push-EDX
 207     # . . call
 208     e8/call  process-string-literal/disp32
 209     # . . discard args
 210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 211     # continue
 212     eb/jump  $convert:next-word/disp8
 213 $convert:regular-word:
 214     # write-slice-buffered(out, word-slice)
 215     # . . push args
 216     52/push-EDX
 217     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 218     # . . call
 219     e8/call  write-slice-buffered/disp32
 220     # . . discard args
 221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 222     # fall through
 223 $convert:next-word:
 224     # write-buffered(out, " ")
 225     # . . push args
 226     68/push  " "/imm32
 227     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 228     # . . call
 229     e8/call  write-buffered/disp32
 230     # . . discard args
 231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 232     # loop
 233     eb/jump  $convert:word-loop/disp8
 234 $convert:next-line:
 235     # write-buffered(out, "\n")
 236     # . . push args
 237     68/push  Newline/imm32
 238     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 239     # . . call
 240     e8/call  write-buffered/disp32
 241     # . . discard args
 242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 243     # loop
 244     e9/jump  $convert:line-loop/disp32
 245 $convert:break:
 246     # write-stream-data(out, new-data-segment)
 247     # . . push args
 248     57/push-EDI
 249     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 250     # . . call
 251     e8/call  write-stream-data/disp32
 252     # . . discard args
 253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 254     # flush(out)
 255     # . . push args
 256     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 257     # . . call
 258     e8/call  flush/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 261 $convert:end:
 262     # . reclaim locals
 263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 264     # . restore registers
 265     5f/pop-to-EDI
 266     5e/pop-to-ESI
 267     5b/pop-to-EBX
 268     5a/pop-to-EDX
 269     59/pop-to-ECX
 270     58/pop-to-EAX
 271     # . epilog
 272     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 273     5d/pop-to-EBP
 274     c3/return
 275 
 276 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
 277 # label, and write the new label out to 'out'.
 278 process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
 279     # pseudocode:
 280     #   print(out-segment, "_string#{Next-string-literal}:\n")
 281     #   emit-string-literal-data(out-segment, string-literal)
 282     #   print(out, "_string#{Next-string-literal}")
 283     #   emit-metadata(out, string-literal)
 284     #   ++ *Next-string-literal
 285     #
 286     # . prolog
 287     55/push-EBP
 288     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 289     # . save registers
 290     51/push-ECX
 291     # var int32-stream/ECX = stream(10)  # number of decimal digits a 32-bit number can have
 292     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa/imm32         # subtract from ESP
 293     68/push  0xa/imm32/decimal-digits-in-32bit-number
 294     68/push  0/imm32/read
 295     68/push  0/imm32/write
 296     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 297     # print(out-segment, "_string#{Next-string-literal}:\n")
 298     # . write(out-segment, "_string")
 299     # . . push args
 300     68/push  "_string"/imm32
 301     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 302     # . . call
 303     e8/call  write/disp32
 304     # . . discard args
 305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 306     # . print-int32-decimal(out-segment, *Next-string-literal)
 307     # . . push args
 308     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 309     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 310     # . . call
 311     e8/call  print-int32-decimal/disp32
 312     # . . discard args
 313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 314     # . write(out-segment, ":\n")
 315     # . . push args
 316     68/push  ":\n"/imm32
 317     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 318     # . . call
 319     e8/call  write/disp32
 320     # . . discard args
 321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 322     # emit-string-literal-data(out-segment, string-literal)
 323     # . . push args
 324     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x8/disp8       .                 # push *(EBP+8)
 325     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 326     # . . call
 327     e8/call  emit-string-literal-data/disp32
 328     # . . discard args
 329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 330     # write(out-segment, "\n")
 331     # . . push args
 332     68/push  Newline/imm32
 333     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 334     # . . call
 335     e8/call  write/disp32
 336     # . . discard args
 337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 338     # print(out, "_string#{Next-string-literal}")
 339     # . write-buffered(out, "_string")
 340     # . . push args
 341     68/push  "_string"/imm32
 342     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 343     # . . call
 344     e8/call  write-buffered/disp32
 345     # . . discard args
 346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 347     # . print-int32-decimal(int32-stream, *Next-string-literal)
 348     # . . push args
 349     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 350     51/push-ECX
 351     # . . call
 352     e8/call  print-int32-decimal/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 355     # . write-stream-data(out, int32-stream)
 356     # . . push args
 357     51/push-ECX
 358     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 359     # . . call
 360     e8/call  write-stream-data/disp32
 361     # . . discard args
 362     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 363     # emit-metadata(out, string-literal)
 364     # . . push args
 365     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 366     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 367     # . . call
 368     e8/call  emit-metadata/disp32
 369     # . . discard args
 370     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 371     # ++ *Next-string-literal
 372     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
 373 $process-string-literal:end:
 374     # . reclaim locals
 375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x16/imm32        # add to ESP
 376     # . restore registers
 377     59/pop-to-ECX
 378     # . epilog
 379     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 380     5d/pop-to-EBP
 381     c3/return
 382 
 383 test-convert-is-idempotent-by-default:
 384     # . prolog
 385     55/push-EBP
 386     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 387     # setup
 388     # . clear-stream(_test-input-stream)
 389     # . . push args
 390     68/push  _test-input-stream/imm32
 391     # . . call
 392     e8/call  clear-stream/disp32
 393     # . . discard args
 394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 395     # . clear-stream(_test-input-buffered-file+4)
 396     # . . push args
 397     b8/copy-to-EAX  _test-input-buffered-file/imm32
 398     05/add-to-EAX  4/imm32
 399     50/push-EAX
 400     # . . call
 401     e8/call  clear-stream/disp32
 402     # . . discard args
 403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 404     # . clear-stream(_test-output-stream)
 405     # . . push args
 406     68/push  _test-output-stream/imm32
 407     # . . call
 408     e8/call  clear-stream/disp32
 409     # . . discard args
 410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 411     # . clear-stream(_test-output-buffered-file+4)
 412     # . . push args
 413     b8/copy-to-EAX  _test-output-buffered-file/imm32
 414     05/add-to-EAX  4/imm32
 415     50/push-EAX
 416     # . . call
 417     e8/call  clear-stream/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 420     # initialize input (meta comments in parens)
 421     #   # comment 1
 422     #     # comment 2 indented
 423     #   == code 0x1  (new segment)
 424     #   # comment 3 inside a segment
 425     #   1
 426     #                         (empty line)
 427     #   2 3 # comment 4 inline with other contents
 428     #   == data 0x2  (new segment)
 429     #   4 5/imm32
 430     # . write(_test-input-stream, "# comment 1\n")
 431     # . . push args
 432     68/push  "# comment 1\n"/imm32
 433     68/push  _test-input-stream/imm32
 434     # . . call
 435     e8/call  write/disp32
 436     # . . discard args
 437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 438     # . write(_test-input-stream, "  # comment 2 indented\n")
 439     # . . push args
 440     68/push  "  # comment 2 indented\n"/imm32
 441     68/push  _test-input-stream/imm32
 442     # . . call
 443     e8/call  write/disp32
 444     # . . discard args
 445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 446     # . write(_test-input-stream, "== code 0x1\n")
 447     # . . push args
 448     68/push  "== code 0x1\n"/imm32
 449     68/push  _test-input-stream/imm32
 450     # . . call
 451     e8/call  write/disp32
 452     # . . discard args
 453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 454     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 455     # . . push args
 456     68/push  "# comment 3 inside a segment\n"/imm32
 457     68/push  _test-input-stream/imm32
 458     # . . call
 459     e8/call  write/disp32
 460     # . . discard args
 461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 462     # . write(_test-input-stream, "1\n")
 463     # . . push args
 464     68/push  "1\n"/imm32
 465     68/push  _test-input-stream/imm32
 466     # . . call
 467     e8/call  write/disp32
 468     # . . discard args
 469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 470     # . write(_test-input-stream, "\n")  # empty line
 471     # . . push args
 472     68/push  "\n"/imm32
 473     68/push  _test-input-stream/imm32
 474     # . . call
 475     e8/call  write/disp32
 476     # . . discard args
 477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 478     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 479     # . . push args
 480     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 481     68/push  _test-input-stream/imm32
 482     # . . call
 483     e8/call  write/disp32
 484     # . . discard args
 485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 486     # . write(_test-input-stream, "== data 0x2\n")
 487     # . . push args
 488     68/push  "== data 0x2\n"/imm32
 489     68/push  _test-input-stream/imm32
 490     # . . call
 491     e8/call  write/disp32
 492     # . . discard args
 493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 494     # . write(_test-input-stream, "4 5/imm32\n")
 495     # . . push args
 496     68/push  "4 5/imm32\n"/imm32
 497     68/push  _test-input-stream/imm32
 498     # . . call
 499     e8/call  write/disp32
 500     # . . discard args
 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 502     # convert(_test-input-buffered-file, _test-output-buffered-file)
 503     # . . push args
 504     68/push  _test-output-buffered-file/imm32
 505     68/push  _test-input-buffered-file/imm32
 506     # . . call
 507     e8/call  convert/disp32
 508     # . . discard args
 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 510     # . flush(_test-output-buffered-file)
 511     # . . push args
 512     68/push  _test-output-buffered-file/imm32
 513     # . . call
 514     e8/call  flush/disp32
 515     # . . discard args
 516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 517     # check output
 518     #     (comment dropped for now)
 519     #     (comment dropped for now)
 520     #   == code 0x1
 521     #     (comment dropped for now)
 522     #   1
 523     #     (comment dropped for now)
 524     #   2 3
 525     #   == data 0x2
 526     #   4 5/imm32
 527     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 528 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 554     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 555     # . . push args
 556     68/push  "F - test-convert-is-idempotent-by-default/0"/imm32
 557     68/push  ""/imm32
 558     68/push  _test-output-stream/imm32
 559     # . . call
 560     e8/call  check-next-stream-line-equal/disp32
 561     # . . discard args
 562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 563     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 564     # . . push args
 565     68/push  "F - test-convert-is-idempotent-by-default/1"/imm32
 566     68/push  ""/imm32
 567     68/push  _test-output-stream/imm32
 568     # . . call
 569     e8/call  check-next-stream-line-equal/disp32
 570     # . . discard args
 571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 572     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 573     # . . push args
 574     68/push  "F - test-convert-is-idempotent-by-default/2"/imm32
 575     68/push  "== code 0x1 "/imm32
 576     68/push  _test-output-stream/imm32
 577     # . . call
 578     e8/call  check-next-stream-line-equal/disp32
 579     # . . discard args
 580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 581     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 582     # . . push args
 583     68/push  "F - test-convert-is-idempotent-by-default/3"/imm32
 584     68/push  ""/imm32
 585     68/push  _test-output-stream/imm32
 586     # . . call
 587     e8/call  check-next-stream-line-equal/disp32
 588     # . . discard args
 589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 590     # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
 591     # . . push args
 592     68/push  "F - test-convert-is-idempotent-by-default/4"/imm32
 593     68/push  "1 "/imm32
 594     68/push  _test-output-stream/imm32
 595     # . . call
 596     e8/call  check-next-stream-line-equal/disp32
 597     # . . discard args
 598     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 599     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 600     # . . push args
 601     68/push  "F - test-convert-is-idempotent-by-default/5"/imm32
 602     68/push  ""/imm32
 603     68/push  _test-output-stream/imm32
 604     # . . call
 605     e8/call  check-next-stream-line-equal/disp32
 606     # . . discard args
 607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 608     # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
 609     # . . push args
 610     68/push  "F - test-convert-is-idempotent-by-default/6"/imm32
 611     68/push  "2 3 "/imm32
 612     68/push  _test-output-stream/imm32
 613     # . . call
 614     e8/call  check-next-stream-line-equal/disp32
 615     # . . discard args
 616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 617     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
 618     # . . push args
 619     68/push  "F - test-convert-is-idempotent-by-default/7"/imm32
 620     68/push  "== data 0x2 "/imm32
 621     68/push  _test-output-stream/imm32
 622     # . . call
 623     e8/call  check-next-stream-line-equal/disp32
 624     # . . discard args
 625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 626     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
 627     # . . push args
 628     68/push  "F - test-convert-is-idempotent-by-default/8"/imm32
 629     68/push  "4 5/imm32 "/imm32
 630     68/push  _test-output-stream/imm32
 631     # . . call
 632     e8/call  check-next-stream-line-equal/disp32
 633     # . . discard args
 634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 635     # . epilog
 636     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 637     5d/pop-to-EBP
 638     c3/return
 639 
 640 test-convert-processes-string-literals:
 641     # . prolog
 642     55/push-EBP
 643     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 644     # setup
 645     # . clear-stream(_test-input-stream)
 646     # . . push args
 647     68/push  _test-input-stream/imm32
 648     # . . call
 649     e8/call  clear-stream/disp32
 650     # . . discard args
 651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 652     # . clear-stream(_test-input-buffered-file+4)
 653     # . . push args
 654     b8/copy-to-EAX  _test-input-buffered-file/imm32
 655     05/add-to-EAX  4/imm32
 656     50/push-EAX
 657     # . . call
 658     e8/call  clear-stream/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 661     # . clear-stream(_test-output-stream)
 662     # . . push args
 663     68/push  _test-output-stream/imm32
 664     # . . call
 665     e8/call  clear-stream/disp32
 666     # . . discard args
 667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 668     # . clear-stream(_test-output-buffered-file+4)
 669     # . . push args
 670     b8/copy-to-EAX  _test-output-buffered-file/imm32
 671     05/add-to-EAX  4/imm32
 672     50/push-EAX
 673     # . . call
 674     e8/call  clear-stream/disp32
 675     # . . discard args
 676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 677     # initialize input (meta comments in parens)
 678     #   == code  (new segment)
 679     #   1 "a"/x
 680     #   2 "bc"/y
 681     68/push  "== code 0x1\n"/imm32
 682     68/push  _test-input-stream/imm32
 683     # . . call
 684     e8/call  write/disp32
 685     # . . discard args
 686     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 687     # . write(_test-input-stream, "1 \"a\"/x\n")
 688     # . . push args
 689     68/push  "1 \"a\"/x\n"/imm32
 690     68/push  _test-input-stream/imm32
 691     # . . call
 692     e8/call  write/disp32
 693     # . . discard args
 694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 695     # . write(_test-input-stream, "2 \"bc\"/y\n")
 696     # . . push args
 697     68/push  "2 \"bc\"/y\n"/imm32
 698     68/push  _test-input-stream/imm32
 699     # . . call
 700     e8/call  write/disp32
 701     # . . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 703     # convert(_test-input-buffered-file, _test-output-buffered-file)
 704     # . . push args
 705     68/push  _test-output-buffered-file/imm32
 706     68/push  _test-input-buffered-file/imm32
 707     # . . call
 708     e8/call  convert/disp32
 709     # . . discard args
 710     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 711     # . flush(_test-output-buffered-file)
 712     # . . push args
 713     68/push  _test-output-buffered-file/imm32
 714     # . . call
 715     e8/call  flush/disp32
 716     # . . discard args
 717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 718     # check output
 719     #   == code 0x1
 720     #   1 _string1/x
 721     #   2 _string2/y
 722     #   == data
 723     #   _string1:
 724     #   1/imm32 61/a
 725     #   _string2:
 726     #   2/imm32 62/b 63/c
 727     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 728     #
 729     # Open question: how to make this check more robust.
 730     # We don't actually care what the auto-generated string variables are
 731     # called. We just want to make sure instructions using string literals
 732     # switch to a string variable with the right value.
 733     # (Modifying string literals completely off the radar for now.)
 734 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 767     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 768     # . . push args
 769     68/push  "F - test-convert-processes-string-literals/0"/imm32
 770     68/push  "== code 0x1 "/imm32
 771     68/push  _test-output-stream/imm32
 772     # . . call
 773     e8/call  check-next-stream-line-equal/disp32
 774     # . . discard args
 775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 776     # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
 777     # . . push args
 778     68/push  "F - test-convert-processes-string-literals/1"/imm32
 779     68/push  "1 _string1/x "/imm32
 780     68/push  _test-output-stream/imm32
 781     # . . call
 782     e8/call  check-next-stream-line-equal/disp32
 783     # . . discard args
 784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 785     # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
 786     # . . push args
 787     68/push  "F - test-convert-processes-string-literals/2"/imm32
 788     68/push  "2 _string2/y "/imm32
 789     68/push  _test-output-stream/imm32
 790     # . . call
 791     e8/call  check-next-stream-line-equal/disp32
 792     # . . discard args
 793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 794     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
 795     # . . push args
 796     68/push  "F - test-convert-processes-string-literals/3"/imm32
 797     68/push  "== data"/imm32
 798     68/push  _test-output-stream/imm32
 799     # . . call
 800     e8/call  check-next-stream-line-equal/disp32
 801     # . . discard args
 802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 803     # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
 804     # . . push args
 805     68/push  "F - test-convert-processes-string-literals/4"/imm32
 806     68/push  "_string1:"/imm32
 807     68/push  _test-output-stream/imm32
 808     # . . call
 809     e8/call  check-next-stream-line-equal/disp32
 810     # . . discard args
 811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 812     # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
 813     # . . push args
 814     68/push  "F - test-convert-processes-string-literals/5"/imm32
 815     68/push  "0x00000001/imm32 61/a "/imm32
 816     68/push  _test-output-stream/imm32
 817     # . . call
 818     e8/call  check-next-stream-line-equal/disp32
 819     # . . discard args
 820     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 821     # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
 822     # . . push args
 823     68/push  "F - test-convert-processes-string-literals/6"/imm32
 824     68/push  "_string2:"/imm32
 825     68/push  _test-output-stream/imm32
 826     # . . call
 827     e8/call  check-next-stream-line-equal/disp32
 828     # . . discard args
 829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 830     # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
 831     # . . push args
 832     68/push  "F - test-convert-processes-string-literals/7"/imm32
 833     68/push  "0x00000002/imm32 62/b 63/c "/imm32
 834     68/push  _test-output-stream/imm32
 835     # . . call
 836     e8/call  check-next-stream-line-equal/disp32
 837     # . . discard args
 838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 839     # . epilog
 840     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 841     5d/pop-to-EBP
 842     c3/return
 843 
 844 # generate the data segment contents byte by byte for a given slice
 845 emit-string-literal-data:  # out : (address stream), word : (address slice)
 846     # pseudocode
 847     #   len = string-length-at-start-of-slice(word->start, word->end)
 848     #   print(out, "#{len}/imm32 ")
 849     #   curr = word->start
 850     #   ++curr  # skip '"'
 851     #   while true
 852     #     if (curr >= word->end) break
 853     #     c = *curr
 854     #     if (c == '"') break
 855     #     if (c == '\') {
 856     #       ++curr
 857     #       c = *curr
 858     #       if (c == 'n')
 859     #         c = newline
 860     #     }
 861     #     append-byte-hex(out, c)
 862     #     if c is alphanumeric:
 863     #       write(out, "/")
 864     #       append-byte(out, c)
 865     #     write(out, " ")
 866     #     ++curr
 867     #
 868     # . prolog
 869     55/push-EBP
 870     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 871     # . save registers
 872     50/push-EAX
 873     51/push-ECX
 874     52/push-EDX
 875     56/push-ESI
 876     # ESI = word
 877     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
 878     # curr/EDX = word->start
 879     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 880     # max/ESI = word->end
 881     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
 882 $emit-string-literal-data:emit-length:
 883     # len/EAX = string-length-at-start-of-slice(word->start, word->end)
 884     # . . push args
 885     56/push-ESI
 886     52/push-EDX
 887     # . . call
 888     e8/call  string-length-at-start-of-slice/disp32
 889     # . . discard args
 890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 891     # print(out, "#{len}/imm32 ")
 892     # . print-int32(out, len)
 893     # . . push args
 894     50/push-EAX
 895     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 896     # . . call
 897     e8/call  print-int32/disp32
 898     # . . discard args
 899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 900     # . write(out, "/imm32 ")
 901     # . . push args
 902     68/push  "/imm32 "/imm32
 903     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 904     # . . call
 905     e8/call  write/disp32
 906     # . . discard args
 907     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 908 $emit-string-literal-data:loop-init:
 909     # ++curr  # skip initial '"'
 910     42/increment-EDX
 911     # c/ECX = 0
 912     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
 913 $emit-string-literal-data:loop:
 914     # if (curr >= max) break
 915     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
 916     0f 83/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp32
 917     # CL = *curr
 918     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
 919     # if (c == '"') break
 920     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x22/imm32/dquote # compare ECX
 921     74/jump-if-equal  $emit-string-literal-data:end/disp8
 922     # if (c != '\') goto emit
 923     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x5c/imm32/backslash  # compare ECX
 924     75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
 925     # ++curr
 926     42/increment-EDX
 927     # if (curr >= max) break
 928     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
 929     73/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp8
 930     # c = *curr
 931     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
 932     # if (c == 'n') c = newline
 933     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x6e/imm32/n      # compare ECX
 934     75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
 935     b9/copy-to-ECX  0x0a/imm32/newline
 936 $emit-string-literal-data:emit:
 937     # append-byte-hex(out, CL)
 938     # . . push args
 939     51/push-ECX
 940     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 941     # . . call
 942     e8/call  append-byte-hex/disp32
 943     # . . discard args
 944     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 945     # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}")
 946     # . EAX = is-alphanumeric?(CL)
 947     # . . push args
 948     51/push-ECX
 949     # . . call
 950     e8/call  is-alphanumeric?/disp32
 951     # . . discard args
 952     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 953     # . if (EAX == 0) goto char-done
 954     3d/compare-EAX-and  0/imm32
 955     74/jump-if-equal  $emit-string-literal-data:char-done/disp8
 956     # . write(out, "/")
 957     # . . push args
 958     68/push  Slash/imm32
 959     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 960     # . . call
 961     e8/call  write/disp32
 962     # . . discard args
 963     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 964     # . append-byte(out, *curr)
 965     # . . push args
 966     51/push-ECX
 967     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 968     # . . call
 969     e8/call  append-byte/disp32
 970     # . . discard args
 971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 972 $emit-string-literal-data:char-done:
 973     # write(out, " ")
 974     # . . push args
 975     68/push  Space/imm32
 976     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 977     # . . call
 978     e8/call  write/disp32
 979     # . . discard args
 980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 981     # ++curr
 982     42/increment-EDX
 983     e9/jump $emit-string-literal-data:loop/disp32
 984 $emit-string-literal-data:end:
 985     # . restore registers
 986     5e/pop-to-ESI
 987     5a/pop-to-EDX
 988     59/pop-to-ECX
 989     58/pop-to-EAX
 990     # . epilog
 991     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 992     5d/pop-to-EBP
 993     c3/return
 994 
 995 is-alphanumeric?:  # c : int -> EAX : boolean
 996     # . prolog
 997     55/push-EBP
 998     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 999     # EAX = c
1000     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
1001     # if (EAX < '0') return false
1002     3d/compare-EAX-with  0x30/imm32/0
1003     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1004     # if (EAX <= '9') return true
1005     3d/compare-EAX-with  0x39/imm32/9
1006     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1007     # if (EAX < 'A') return false
1008     3d/compare-EAX-with  0x41/imm32/A
1009     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1010     # if (EAX <= 'Z') return true
1011     3d/compare-EAX-with  0x5a/imm32/Z
1012     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1013     # if (EAX < 'a') return false
1014     3d/compare-EAX-with  0x61/imm32/a
1015     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1016     # if (EAX <= 'z') return true
1017     3d/compare-EAX-with  0x7a/imm32/z
1018     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1019     # return false
1020 $is-alphanumeric?:false:
1021     b8/copy-to-EAX  0/imm32/false
1022     eb/jump  $is-alphanumeric?:end/disp8
1023 $is-alphanumeric?:true:
1024     b8/copy-to-EAX  1/imm32/true
1025 $is-alphanumeric?:end:
1026     # . epilog
1027     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1028     5d/pop-to-EBP
1029     c3/return
1030 
1031 test-emit-string-literal-data:
1032     # . prolog
1033     55/push-EBP
1034     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1035     # setup
1036     # . clear-stream(_test-output-stream)
1037     # . . push args
1038     68/push  _test-output-stream/imm32
1039     # . . call
1040     e8/call  clear-stream/disp32
1041     # . . discard args
1042     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1043     # var slice/ECX = '"abc"/d'
1044     68/push  _test-slice-abc-limit/imm32
1045     68/push  _test-slice-abc/imm32
1046     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1047     # emit-string-literal-data(_test-output-stream, slice)
1048     # . . push args
1049     51/push-ECX
1050     68/push  _test-output-stream/imm32
1051     # . . call
1052     e8/call  emit-string-literal-data/disp32
1053     # . . discard args
1054     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1055 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1081     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg)
1082     # . . push args
1083     68/push  "F - test-emit-string-literal-data"/imm32
1084     68/push  "0x00000003/imm32 61/a 62/b 63/c "/imm32
1085     68/push  _test-output-stream/imm32
1086     # . . call
1087     e8/call  check-stream-equal/disp32
1088     # . . discard args
1089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1090     # . epilog
1091     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1092     5d/pop-to-EBP
1093     c3/return
1094 
1095 test-emit-string-literal-data-empty:
1096     # . prolog
1097     55/push-EBP
1098     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1099     # setup
1100     # . clear-stream(_test-output-stream)
1101     # . . push args
1102     68/push  _test-output-stream/imm32
1103     # . . call
1104     e8/call  clear-stream/disp32
1105     # . . discard args
1106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1107     # var slice/ECX = '""'
1108     68/push  0/imm32/end
1109     68/push  0/imm32/start
1110     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1111     # emit-string-literal-data(_test-output-stream, slice)
1112     # . . push args
1113     51/push-ECX
1114     68/push  _test-output-stream/imm32
1115     # . . call
1116     e8/call  emit-string-literal-data/disp32
1117     # . . discard args
1118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1119 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1145     # . check-stream-equal(_test-output-stream, "0/imm32 ", msg)
1146     # . . push args
1147     68/push  "F - test-emit-string-literal-data-empty"/imm32
1148     68/push  "0x00000000/imm32 "/imm32
1149     68/push  _test-output-stream/imm32
1150     # . . call
1151     e8/call  check-stream-equal/disp32
1152     # . . discard args
1153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1154     # . epilog
1155     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1156     5d/pop-to-EBP
1157     c3/return
1158 
1159 # just to keep things simple
1160 test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
1161     # . prolog
1162     55/push-EBP
1163     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1164     # setup
1165     # . clear-stream(_test-output-stream)
1166     # . . push args
1167     68/push  _test-output-stream/imm32
1168     # . . call
1169     e8/call  clear-stream/disp32
1170     # . . discard args
1171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1172     # var slice/ECX = '"a b"'
1173     68/push  _test-slice-a-space-b-limit/imm32
1174     68/push  _test-slice-a-space-b/imm32
1175     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1176     # emit-string-literal-data(_test-output-stream, slice)
1177     # . . push args
1178     51/push-ECX
1179     68/push  _test-output-stream/imm32
1180     # . . call
1181     e8/call  emit-string-literal-data/disp32
1182     # . . discard args
1183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1184 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1210     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 20 62/b ", msg)  # ideally we'd like to say '20/space' but that requires managing names for codepoints
1211     # . . push args
1212     68/push  "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32
1213     68/push  "0x00000003/imm32 61/a 20 62/b "/imm32
1214     68/push  _test-output-stream/imm32
1215     # . . call
1216     e8/call  check-stream-equal/disp32
1217     # . . discard args
1218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1219     # . epilog
1220     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1221     5d/pop-to-EBP
1222     c3/return
1223 
1224 test-emit-string-literal-data-handles-escape-sequences:
1225     # . prolog
1226     55/push-EBP
1227     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1228     # setup
1229     # . clear-stream(_test-output-stream)
1230     # . . push args
1231     68/push  _test-output-stream/imm32
1232     # . . call
1233     e8/call  clear-stream/disp32
1234     # . . discard args
1235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1236     # var slice/ECX = '"a\"b"'
1237     68/push  _test-slice-a-dquote-b-limit/imm32
1238     68/push  _test-slice-a-dquote-b/imm32
1239     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1240     # emit-string-literal-data(_test-output-stream, slice)
1241     # . . push args
1242     51/push-ECX
1243     68/push  _test-output-stream/imm32
1244     # . . call
1245     e8/call  emit-string-literal-data/disp32
1246     # . . discard args
1247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1248 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1274     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg)
1275     # . . push args
1276     68/push  "F - test-emit-string-literal-data-handles-escape-sequences"/imm32
1277     68/push  "0x00000003/imm32 61/a 22 62/b "/imm32
1278     68/push  _test-output-stream/imm32
1279     # . . call
1280     e8/call  check-stream-equal/disp32
1281     # . . discard args
1282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1283     # . epilog
1284     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1285     5d/pop-to-EBP
1286     c3/return
1287 
1288 test-emit-string-literal-data-handles-newline-escape:
1289     # . prolog
1290     55/push-EBP
1291     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1292     # setup
1293     # . clear-stream(_test-output-stream)
1294     # . . push args
1295     68/push  _test-output-stream/imm32
1296     # . . call
1297     e8/call  clear-stream/disp32
1298     # . . discard args
1299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1300     # var slice/ECX = '"a\nb"'
1301     68/push  _test-slice-a-newline-b-limit/imm32
1302     68/push  _test-slice-a-newline-b/imm32
1303     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1304     # emit-string-literal-data(_test-output-stream, slice)
1305     # . . push args
1306     51/push-ECX
1307     68/push  _test-output-stream/imm32
1308     # . . call
1309     e8/call  emit-string-literal-data/disp32
1310     # . . discard args
1311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1312 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1338     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 0a 62/b ", msg)
1339     # . . push args
1340     68/push  "F - test-emit-string-literal-data-handles-newline-escape"/imm32
1341     68/push  "0x00000003/imm32 61/a 0a 62/b "/imm32
1342     68/push  _test-output-stream/imm32
1343     # . . call
1344     e8/call  check-stream-equal/disp32
1345     # . . discard args
1346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1347     # . epilog
1348     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1349     5d/pop-to-EBP
1350     c3/return
1351 
1352 # emit everything from a word except the initial datum
1353 emit-metadata:  # out : (address buffered-file), word : (address slice)
1354     # pseudocode
1355     #   var slice = {0, word->end}
1356     #   curr = word->start
1357     #   if *curr == '"'
1358     #     curr = skip-string-in-slice(curr, word->end)
1359     #   else
1360     #     while true
1361     #       if curr == word->end
1362     #         return
1363     #       if *curr == '/'
1364     #         break
1365     #       ++curr
1366     #   slice->start = curr
1367     #   write-slice-buffered(out, slice)
1368     #
1369     # . prolog
1370     55/push-EBP
1371     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1372     # . save registers
1373     50/push-EAX
1374     51/push-ECX
1375     52/push-EDX
1376     53/push-EBX
1377     56/push-ESI
1378     # ESI = word
1379     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
1380     # curr/ECX = word->start
1381     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1382     # end/EDX = word->end
1383     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
1384     # var slice/EBX = {0, end}
1385     52/push-EDX
1386     68/push  0/imm32
1387     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
1388     # EAX = 0
1389     b8/copy-to-EAX  0/imm32
1390 $emit-metadata:check-for-string-literal:
1391     # -  if (*curr == '"') curr = skip-string-in-slice(curr, end)
1392     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
1393     3d/compare-EAX-and  0x22/imm32/dquote
1394     75/jump-if-not-equal  $emit-metadata:skip-datum-loop/disp8
1395 $emit-metadata:skip-string-literal:
1396     # . EAX = skip-string-in-slice(curr, end)
1397     # . . push args
1398     52/push-EDX
1399     51/push-ECX
1400     # . . call
1401     e8/call  skip-string-in-slice/disp32
1402     # . . discard args
1403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1404     # . curr = EAX
1405     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
1406     eb/jump  $emit-metadata:emit/disp8
1407 $emit-metadata:skip-datum-loop:
1408     # - otherwise scan for '/'
1409     # if (curr == end) return
1410     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
1411     74/jump-if-equal  $emit-metadata:end/disp8
1412     # if (*curr == '/') break
1413     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
1414     3d/compare-EAX-and  0x2f/imm32/slash
1415     74/jump-if-equal  $emit-metadata:emit/disp8
1416     # ++curr
1417     41/increment-ECX
1418     eb/jump  $emit-metadata:skip-datum-loop/disp8
1419 $emit-metadata:emit:
1420     # slice->start = ECX
1421     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EBX
1422     # write-slice-buffered(out, slice)
1423     # . . push args
1424     53/push-EBX
1425     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1426     # . . call
1427     e8/call  write-slice-buffered/disp32
1428     # . . discard args
1429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
1430 $emit-metadata:end:
1431     # . reclaim locals
1432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           8/imm32      .                    # add to ESP
1433     # . restore registers
1434     5e/pop-to-ESI
1435     5b/pop-to-EBX
1436     5a/pop-to-EDX
1437     59/pop-to-ECX
1438     58/pop-to-EAX
1439     # . epilog
1440     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1441     5d/pop-to-EBP
1442     c3/return
1443 
1444 test-emit-metadata:
1445     # . prolog
1446     55/push-EBP
1447     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1448     # setup
1449     # . clear-stream(_test-output-stream)
1450     # . . push args
1451     68/push  _test-output-stream/imm32
1452     # . . call
1453     e8/call  clear-stream/disp32
1454     # . . discard args
1455     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1456     # . clear-stream(_test-output-buffered-file+4)
1457     # . . push args
1458     b8/copy-to-EAX  _test-output-buffered-file/imm32
1459     05/add-to-EAX  4/imm32
1460     50/push-EAX
1461     # . . call
1462     e8/call  clear-stream/disp32
1463     # . . discard args
1464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1465     # (EAX..ECX) = "abc/def"
1466     b8/copy-to-EAX  "abc/def"/imm32
1467     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1468     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
1469     05/add-to-EAX  4/imm32
1470     # var slice/ECX = {EAX, ECX}
1471     51/push-ECX
1472     50/push-EAX
1473     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1474     # emit-metadata(_test-output-buffered-file, slice)
1475     # . . push args
1476     51/push-ECX
1477     68/push  _test-output-buffered-file/imm32
1478     # . . call
1479     e8/call  emit-metadata/disp32
1480     # . . discard args
1481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1482     # flush(_test-output-buffered-file)
1483     # . . push args
1484     68/push  _test-output-buffered-file/imm32
1485     # . . call
1486     e8/call  flush/disp32
1487     # . . discard args
1488     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1489     # check-stream-equal(_test-output-stream, "/def", msg)  # important that there's no leading space
1490     # . . push args
1491     68/push  "F - test-emit-metadata"/imm32
1492     68/push  "/def"/imm32
1493     68/push  _test-output-stream/imm32
1494     # . . call
1495     e8/call  check-stream-equal/disp32
1496     # . . discard args
1497     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1498     # . epilog
1499     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1500     5d/pop-to-EBP
1501     c3/return
1502 
1503 test-emit-metadata-none:
1504     # . prolog
1505     55/push-EBP
1506     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1507     # setup
1508     # . clear-stream(_test-output-stream)
1509     # . . push args
1510     68/push  _test-output-stream/imm32
1511     # . . call
1512     e8/call  clear-stream/disp32
1513     # . . discard args
1514     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1515     # . clear-stream(_test-output-buffered-file+4)
1516     # . . push args
1517     b8/copy-to-EAX  _test-output-buffered-file/imm32
1518     05/add-to-EAX  4/imm32
1519     50/push-EAX
1520     # . . call
1521     e8/call  clear-stream/disp32
1522     # . . discard args
1523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1524     # (EAX..ECX) = "abc"
1525     b8/copy-to-EAX  "abc"/imm32
1526     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1527     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
1528     05/add-to-EAX  4/imm32
1529     # var slice/ECX = {EAX, ECX}
1530     51/push-ECX
1531     50/push-EAX
1532     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1533     # emit-metadata(_test-output-buffered-file, slice)
1534     # . . push args
1535     51/push-ECX
1536     68/push  _test-output-buffered-file/imm32
1537     # . . call
1538     e8/call  emit-metadata/disp32
1539     # . . discard args
1540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1541     # flush(_test-output-buffered-file)
1542     # . . push args
1543     68/push  _test-output-buffered-file/imm32
1544     # . . call
1545     e8/call  flush/disp32
1546     # . . discard args
1547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1548     # check-stream-equal(_test-output-stream, "", msg)
1549     # . . push args
1550     68/push  "F - test-emit-metadata-none"/imm32
1551     68/push  ""/imm32
1552     68/push  _test-output-stream/imm32
1553     # . . call
1554     e8/call  check-stream-equal/disp32
1555     # . . discard args
1556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1557     # . epilog
1558     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1559     5d/pop-to-EBP
1560     c3/return
1561 
1562 test-emit-metadata-multiple:
1563     # . prolog
1564     55/push-EBP
1565     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1566     # setup
1567     # . clear-stream(_test-output-stream)
1568     # . . push args
1569     68/push  _test-output-stream/imm32
1570     # . . call
1571     e8/call  clear-stream/disp32
1572     # . . discard args
1573     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1574     # . clear-stream(_test-output-buffered-file+4)
1575     # . . push args
1576     b8/copy-to-EAX  _test-output-buffered-file/imm32
1577     05/add-to-EAX  4/imm32
1578     50/push-EAX
1579     # . . call
1580     e8/call  clear-stream/disp32
1581     # . . discard args
1582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1583     # (EAX..ECX) = "abc/def/ghi"
1584     b8/copy-to-EAX  "abc/def/ghi"/imm32
1585     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1586     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
1587     05/add-to-EAX  4/imm32
1588     # var slice/ECX = {EAX, ECX}
1589     51/push-ECX
1590     50/push-EAX
1591     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1592     # emit-metadata(_test-output-buffered-file, slice)
1593     # . . push args
1594     51/push-ECX
1595     68/push  _test-output-buffered-file/imm32
1596     # . . call
1597     e8/call  emit-metadata/disp32
1598     # . . discard args
1599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1600     # flush(_test-output-buffered-file)
1601     # . . push args
1602     68/push  _test-output-buffered-file/imm32
1603     # . . call
1604     e8/call  flush/disp32
1605     # . . discard args
1606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1607     # check-stream-equal(_test-output-stream, "/def/ghi", msg)  # important that there's no leading space
1608     # . . push args
1609     68/push  "F - test-emit-metadata-multiple"/imm32
1610     68/push  "/def/ghi"/imm32
1611     68/push  _test-output-stream/imm32
1612     # . . call
1613     e8/call  check-stream-equal/disp32
1614     # . . discard args
1615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1616     # . epilog
1617     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1618     5d/pop-to-EBP
1619     c3/return
1620 
1621 test-emit-metadata-when-no-datum:
1622     # . prolog
1623     55/push-EBP
1624     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1625     # setup
1626     # . clear-stream(_test-output-stream)
1627     # . . push args
1628     68/push  _test-output-stream/imm32
1629     # . . call
1630     e8/call  clear-stream/disp32
1631     # . . discard args
1632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1633     # . clear-stream(_test-output-buffered-file+4)
1634     # . . push args
1635     b8/copy-to-EAX  _test-output-buffered-file/imm32
1636     05/add-to-EAX  4/imm32
1637     50/push-EAX
1638     # . . call
1639     e8/call  clear-stream/disp32
1640     # . . discard args
1641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1642     # var slice/ECX = "/abc"
1643     b8/copy-to-EAX  "/abc"/imm32
1644     # . push end/ECX
1645     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1646     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
1647     51/push-ECX
1648     # . push curr/EAX
1649     05/add-to-EAX  4/imm32
1650     50/push-EAX
1651     # . save stack pointer
1652     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1653     # emit-metadata(_test-output-buffered-file, slice)
1654     # . . push args
1655     51/push-ECX
1656     68/push  _test-output-buffered-file/imm32
1657     # . . call
1658     e8/call  emit-metadata/disp32
1659     # . . discard args
1660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1661     # flush(_test-output-buffered-file)
1662     # . . push args
1663     68/push  _test-output-buffered-file/imm32
1664     # . . call
1665     e8/call  flush/disp32
1666     # . . discard args
1667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1668     # check-stream-equal(_test-output-stream, "/abc", msg)  # nothing skipped
1669     # . . push args
1670     68/push  "F - test-emit-metadata-when-no-datum"/imm32
1671     68/push  "/abc"/imm32
1672     68/push  _test-output-stream/imm32
1673     # . . call
1674     e8/call  check-stream-equal/disp32
1675     # . . discard args
1676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1677     # . epilog
1678     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1679     5d/pop-to-EBP
1680     c3/return
1681 
1682 test-emit-metadata-in-string-literal:
1683     # . prolog
1684     55/push-EBP
1685     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1686     # setup
1687     # . clear-stream(_test-output-stream)
1688     # . . push args
1689     68/push  _test-output-stream/imm32
1690     # . . call
1691     e8/call  clear-stream/disp32
1692     # . . discard args
1693     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1694     # . clear-stream(_test-output-buffered-file+4)
1695     # . . push args
1696     b8/copy-to-EAX  _test-output-buffered-file/imm32
1697     05/add-to-EAX  4/imm32
1698     50/push-EAX
1699     # . . call
1700     e8/call  clear-stream/disp32
1701     # . . discard args
1702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1703     # var slice/ECX = "\"abc/def\"/ghi"
1704     68/push  _test-slice-literal-string-with-limit/imm32
1705     68/push  _test-slice-literal-string/imm32/start
1706     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1707     # emit-metadata(_test-output-buffered-file, slice)
1708     # . . push args
1709     51/push-ECX
1710     68/push  _test-output-buffered-file/imm32
1711     # . . call
1712     e8/call  emit-metadata/disp32
1713     # . . discard args
1714     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1715     # flush(_test-output-buffered-file)
1716     # . . push args
1717     68/push  _test-output-buffered-file/imm32
1718     # . . call
1719     e8/call  flush/disp32
1720     # . . discard args
1721     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1722 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1748     # check-stream-equal(_test-output-stream, "/ghi", msg)  # important that there's no leading space
1749     # . . push args
1750     68/push  "F - test-emit-metadata-in-string-literal"/imm32
1751     68/push  "/ghi"/imm32
1752     68/push  _test-output-stream/imm32
1753     # . . call
1754     e8/call  check-stream-equal/disp32
1755     # . . discard args
1756     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1757     # . epilog
1758     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1759     5d/pop-to-EBP
1760     c3/return
1761 
1762 # (re)compute the bounds of the next word in the line
1763 # return empty string on reaching end of file
1764 next-word-or-string:  # line : (address stream byte), out : (address slice)
1765     # . prolog
1766     55/push-EBP
1767     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1768     # . save registers
1769     50/push-EAX
1770     51/push-ECX
1771     56/push-ESI
1772     57/push-EDI
1773     # ESI = line
1774     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
1775     # EDI = out
1776     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
1777     # skip-chars-matching(line, ' ')
1778     # . . push args
1779     68/push  0x20/imm32/space
1780     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1781     # . . call
1782     e8/call  skip-chars-matching/disp32
1783     # . . discard args
1784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1785 $next-word-or-string:check0:
1786     # if (line->read >= line->write) clear out and return
1787     # . EAX = line->read
1788     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
1789     # . if (EAX < line->write) goto next check
1790     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
1791     7c/jump-if-lesser  $next-word-or-string:check-for-comment/disp8
1792     # . return out = {0, 0}
1793     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
1794     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
1795     eb/jump  $next-word-or-string:end/disp8
1796 $next-word-or-string:check-for-comment:
1797     # out->start = &line->data[line->read]
1798     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1799     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
1800     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
1801     # if line->data[line->read] == '#'
1802     # . EAX = line->data[line->read]
1803     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1804     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
1805     # . compare
1806     3d/compare-EAX-and  0x23/imm32/pound
1807     75/jump-if-not-equal  $next-word-or-string:check-for-string-literal/disp8
1808 $next-word-or-string:comment:
1809     # out->end = &line->data[line->write]
1810     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1811     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
1812     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1813     # line->read = line->write  # skip rest of line
1814     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1815     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
1816     # return
1817     eb/jump  $next-word-or-string:end/disp8
1818 $next-word-or-string:check-for-string-literal:
1819     # if line->data[line->read] == '"'
1820     # . EAX = line->data[line->read]
1821     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1822     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
1823     # . compare
1824     3d/compare-EAX-and  0x22/imm32/dquote
1825     75/jump-if-not-equal  $next-word-or-string:regular-word/disp8
1826 $next-word-or-string:string-literal:
1827     # skip-string(line)
1828     # . . push args
1829     56/push-ESI
1830     # . . call
1831     e8/call  skip-string/disp32
1832     # . . discard args
1833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1834     # fall through
1835 $next-word-or-string:regular-word:
1836     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1837     # . . push args
1838     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1839     # . . call
1840     e8/call  skip-chars-not-matching-whitespace/disp32
1841     # . . discard args
1842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1843     # out->end = &line->data[line->read]
1844     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1845     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
1846     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1847 $next-word-or-string:end:
1848     # . restore registers
1849     5f/pop-to-EDI
1850     5e/pop-to-ESI
1851     59/pop-to-ECX
1852     58/pop-to-EAX
1853     # . epilog
1854     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1855     5d/pop-to-EBP
1856     c3/return
1857 
1858 test-next-word-or-string:
1859     # . prolog
1860     55/push-EBP
1861     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1862     # setup
1863     # . clear-stream(_test-input-stream)
1864     # . . push args
1865     68/push  _test-input-stream/imm32
1866     # . . call
1867     e8/call  clear-stream/disp32
1868     # . . discard args
1869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1870     # var slice/ECX = {0, 0}
1871     68/push  0/imm32/end
1872     68/push  0/imm32/start
1873     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1874     # write(_test-input-stream, "  ab")
1875     # . . push args
1876     68/push  "  ab"/imm32
1877     68/push  _test-input-stream/imm32
1878     # . . call
1879     e8/call  write/disp32
1880     # . . discard args
1881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1882     # next-word-or-string(_test-input-stream, slice)
1883     # . . push args
1884     51/push-ECX
1885     68/push  _test-input-stream/imm32
1886     # . . call
1887     e8/call  next-word-or-string/disp32
1888     # . . discard args
1889     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1890     # check-ints-equal(_test-input-stream->read, 4, msg)
1891     # . . push args
1892     68/push  "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
1893     68/push  4/imm32
1894     b8/copy-to-EAX  _test-input-stream/imm32
1895     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
1896     # . . call
1897     e8/call  check-ints-equal/disp32
1898     # . . discard args
1899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1900     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1901     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1902     # . . push args
1903     68/push  "F - test-next-word-or-string: start"/imm32
1904     68/push  0xe/imm32
1905     # . . push slice->start - _test-input-stream
1906     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1907     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1908     50/push-EAX
1909     # . . call
1910     e8/call  check-ints-equal/disp32
1911     # . . discard args
1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1913     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1914     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1915     # . . push args
1916     68/push  "F - test-next-word-or-string: end"/imm32
1917     68/push  0x10/imm32
1918     # . . push slice->end - _test-input-stream
1919     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1920     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1921     50/push-EAX
1922     # . . call
1923     e8/call  check-ints-equal/disp32
1924     # . . discard args
1925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1926     # . epilog
1927     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1928     5d/pop-to-EBP
1929     c3/return
1930 
1931 test-next-word-or-string-returns-whole-comment:
1932     # . prolog
1933     55/push-EBP
1934     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1935     # setup
1936     # . clear-stream(_test-input-stream)
1937     # . . push args
1938     68/push  _test-input-stream/imm32
1939     # . . call
1940     e8/call  clear-stream/disp32
1941     # . . discard args
1942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1943     # var slice/ECX = {0, 0}
1944     68/push  0/imm32/end
1945     68/push  0/imm32/start
1946     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1947     # write(_test-input-stream, "  # a")
1948     # . . push args
1949     68/push  "  # a"/imm32
1950     68/push  _test-input-stream/imm32
1951     # . . call
1952     e8/call  write/disp32
1953     # . . discard args
1954     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1955     # next-word-or-string(_test-input-stream, slice)
1956     # . . push args
1957     51/push-ECX
1958     68/push  _test-input-stream/imm32
1959     # . . call
1960     e8/call  next-word-or-string/disp32
1961     # . . discard args
1962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1963     # check-ints-equal(_test-input-stream->read, 5, msg)
1964     # . . push args
1965     68/push  "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
1966     68/push  5/imm32
1967     b8/copy-to-EAX  _test-input-stream/imm32
1968     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
1969     # . . call
1970     e8/call  check-ints-equal/disp32
1971     # . . discard args
1972     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1973     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1974     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1975     # . . push args
1976     68/push  "F - test-next-word-or-string-returns-whole-comment: start"/imm32
1977     68/push  0xe/imm32
1978     # . . push slice->start - _test-input-stream
1979     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1980     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1981     50/push-EAX
1982     # . . call
1983     e8/call  check-ints-equal/disp32
1984     # . . discard args
1985     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1986     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1987     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1988     # . . push args
1989     68/push  "F - test-next-word-or-string-returns-whole-comment: end"/imm32
1990     68/push  0x11/imm32
1991     # . . push slice->end - _test-input-stream
1992     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1993     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1994     50/push-EAX
1995     # . . call
1996     e8/call  check-ints-equal/disp32
1997     # . . discard args
1998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1999     # . epilog
2000     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2001     5d/pop-to-EBP
2002     c3/return
2003 
2004 test-next-word-or-string-returns-empty-string-on-eof:
2005     # . prolog
2006     55/push-EBP
2007     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2008     # setup
2009     # . clear-stream(_test-input-stream)
2010     # . . push args
2011     68/push  _test-input-stream/imm32
2012     # . . call
2013     e8/call  clear-stream/disp32
2014     # . . discard args
2015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2016     # var slice/ECX = {0, 0}
2017     68/push  0/imm32/end
2018     68/push  0/imm32/start
2019     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2020     # write nothing to _test-input-stream
2021     # next-word-or-string(_test-input-stream, slice)
2022     # . . push args
2023     51/push-ECX
2024     68/push  _test-input-stream/imm32
2025     # . . call
2026     e8/call  next-word-or-string/disp32
2027     # . . discard args
2028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2029     # check-ints-equal(slice->end - slice->start, 0, msg)
2030     # . . push args
2031     68/push  "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32
2032     68/push  0/imm32
2033     # . . push slice->end - slice->start
2034     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
2035     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
2036     50/push-EAX
2037     # . . call
2038     e8/call  check-ints-equal/disp32
2039     # . . discard args
2040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2041     # . epilog
2042     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2043     5d/pop-to-EBP
2044     c3/return
2045 
2046 test-next-word-or-string-returns-whole-string:
2047     # . prolog
2048     55/push-EBP
2049     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2050     # setup
2051     # . clear-stream(_test-input-stream)
2052     # . . push args
2053     68/push  _test-input-stream/imm32
2054     # . . call
2055     e8/call  clear-stream/disp32
2056     # . . discard args
2057     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2058     # var slice/ECX = {0, 0}
2059     68/push  0/imm32/end
2060     68/push  0/imm32/start
2061     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2062     # write(_test-input-stream, " \"a b\"/imm32 ")
2063     # . . push args
2064     68/push  " \"a b\"/imm32 "/imm32
2065     68/push  _test-input-stream/imm32
2066     # . . call
2067     e8/call  write/disp32
2068     # . . discard args
2069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2070     # next-word-or-string(_test-input-stream, slice)
2071     # . . push args
2072     51/push-ECX
2073     68/push  _test-input-stream/imm32
2074     # . . call
2075     e8/call  next-word-or-string/disp32
2076     # . . discard args
2077     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2078     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2079     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2080     # . . push args
2081     68/push  "F - test-next-word-or-string-returns-whole-string: start"/imm32
2082     68/push  0xd/imm32
2083     # . . push slice->start - _test-input-stream
2084     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
2085     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2086     50/push-EAX
2087     # . . call
2088     e8/call  check-ints-equal/disp32
2089     # . . discard args
2090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2091     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2092     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2093     # . . push args
2094     68/push  "F - test-next-word-or-string-returns-whole-string: end"/imm32
2095     68/push  0x18/imm32
2096     # . . push slice->end - _test-input-stream
2097     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
2098     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2099     50/push-EAX
2100     # . . call
2101     e8/call  check-ints-equal/disp32
2102     # . . discard args
2103     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2104     # . epilog
2105     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2106     5d/pop-to-EBP
2107     c3/return
2108 
2109 test-next-word-or-string-returns-string-with-escapes:
2110     # . prolog
2111     55/push-EBP
2112     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2113     # setup
2114     # . clear-stream(_test-input-stream)
2115     # . . push args
2116     68/push  _test-input-stream/imm32
2117     # . . call
2118     e8/call  clear-stream/disp32
2119     # . . discard args
2120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2121     # var slice/ECX = {0, 0}
2122     68/push  0/imm32/end
2123     68/push  0/imm32/start
2124     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2125     # write(_test-input-stream, " \"a\\\"b\"/x")
2126     # . . push args
2127     68/push  " \"a\\\"b\"/x"/imm32
2128     68/push  _test-input-stream/imm32
2129     # . . call
2130     e8/call  write/disp32
2131     # . . discard args
2132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2133     # next-word-or-string(_test-input-stream, slice)
2134     # . . push args
2135     51/push-ECX
2136     68/push  _test-input-stream/imm32
2137     # . . call
2138     e8/call  next-word-or-string/disp32
2139     # . . discard args
2140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2141     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2142     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2143     # . . push args
2144     68/push  "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
2145     68/push  0xd/imm32
2146     # . . push slice->start - _test-input-stream
2147     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
2148     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2149     50/push-EAX
2150     # . . call
2151     e8/call  check-ints-equal/disp32
2152     # . . discard args
2153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2154     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2155     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2156     # . . push args
2157     68/push  "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
2158     68/push  0x15/imm32
2159     # . . push slice->end - _test-input-stream
2160     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
2161     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
2162     50/push-EAX
2163     # . . call
2164     e8/call  check-ints-equal/disp32
2165     # . . discard args
2166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2167     # . epilog
2168     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2169     5d/pop-to-EBP
2170     c3/return
2171 
2172 # update line->read to end of string literal surrounded by double quotes
2173 # line->read must start out at a double-quote
2174 skip-string:  # line : (address stream)
2175     # . prolog
2176     55/push-EBP
2177     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2178     # . save registers
2179     50/push-EAX
2180     51/push-ECX
2181     52/push-EDX
2182     # ECX = line
2183     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2184     # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
2185     # . . push &line->data[line->write]
2186     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   8/disp8         .                 # copy *(ECX+8) to EDX
2187     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
2188     52/push-EDX
2189     # . . push &line->data[line->read]
2190     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
2191     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
2192     52/push-EDX
2193     # . . call
2194     e8/call  skip-string-in-slice/disp32
2195     # . . discard args
2196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2197     # line->read = EAX - line->data
2198     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
2199     2d/subtract-from-EAX  0xc/imm32
2200     89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
2201 $skip-string:end:
2202     # . restore registers
2203     5a/pop-to-EDX
2204     59/pop-to-ECX
2205     58/pop-to-EAX
2206     # . epilog
2207     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2208     5d/pop-to-EBP
2209     c3/return
2210 
2211 test-skip-string:
2212     # . prolog
2213     55/push-EBP
2214     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2215     # setup
2216     # . clear-stream(_test-input-stream)
2217     # . . push args
2218     68/push  _test-input-stream/imm32
2219     # . . call
2220     e8/call  clear-stream/disp32
2221     # . . discard args
2222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2223     # . write(_test-input-stream, "\"abc\" def")
2224     # .                   indices:  0123 45
2225     # . . push args
2226     68/push  "\"abc\" def"/imm32
2227     68/push  _test-input-stream/imm32
2228     # . . call
2229     e8/call  write/disp32
2230     # . . discard args
2231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2232     # precondition: line->read == 0
2233     # . . push args
2234     68/push  "F - test-skip-string/precondition"/imm32
2235     68/push  0/imm32
2236     b8/copy-to-EAX  _test-input-stream/imm32
2237     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2238     # . . call
2239     e8/call  check-ints-equal/disp32
2240     # . . discard args
2241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2242     # skip-string(_test-input-stream)
2243     # . . push args
2244     68/push  _test-input-stream/imm32
2245     # . . call
2246     e8/call  skip-string/disp32
2247     # . . discard args
2248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2249     # check-ints-equal(line->read, 5, msg)
2250     # . . push args
2251     68/push  "F - test-skip-string"/imm32
2252     68/push  5/imm32
2253     b8/copy-to-EAX  _test-input-stream/imm32
2254     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2255     # . . call
2256     e8/call  check-ints-equal/disp32
2257     # . . discard args
2258     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2259     # . epilog
2260     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2261     5d/pop-to-EBP
2262     c3/return
2263 
2264 test-skip-string-ignores-spaces:
2265     # . prolog
2266     55/push-EBP
2267     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2268     # setup
2269     # . clear-stream(_test-input-stream)
2270     # . . push args
2271     68/push  _test-input-stream/imm32
2272     # . . call
2273     e8/call  clear-stream/disp32
2274     # . . discard args
2275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2276     # . write(_test-input-stream, "\"a b\"/yz")
2277     # .                   indices:  0123 45
2278     # . . push args
2279     68/push  "\"a b\"/yz"/imm32
2280     68/push  _test-input-stream/imm32
2281     # . . call
2282     e8/call  write/disp32
2283     # . . discard args
2284     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2285     # precondition: line->read == 0
2286     # . . push args
2287     68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
2288     68/push  0/imm32
2289     b8/copy-to-EAX  _test-input-stream/imm32
2290     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2291     # . . call
2292     e8/call  check-ints-equal/disp32
2293     # . . discard args
2294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2295     # skip-string(_test-input-stream)
2296     # . . push args
2297     68/push  _test-input-stream/imm32
2298     # . . call
2299     e8/call  skip-string/disp32
2300     # . . discard args
2301     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2302     # check-ints-equal(line->read, 5, msg)
2303     # . . push args
2304     68/push  "F - test-skip-string-ignores-spaces"/imm32
2305     68/push  5/imm32
2306     b8/copy-to-EAX  _test-input-stream/imm32
2307     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2308     # . . call
2309     e8/call  check-ints-equal/disp32
2310     # . . discard args
2311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2312     # . epilog
2313     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2314     5d/pop-to-EBP
2315     c3/return
2316 
2317 test-skip-string-ignores-escapes:
2318     # . prolog
2319     55/push-EBP
2320     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2321     # setup
2322     # . clear-stream(_test-input-stream)
2323     # . . push args
2324     68/push  _test-input-stream/imm32
2325     # . . call
2326     e8/call  clear-stream/disp32
2327     # . . discard args
2328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2329     # . write(_test-input-stream, "\"a\\\"b\"/yz")
2330     # .                   indices:  01 2 34 56
2331     # . . push args
2332     68/push  "\"a\\\"b\"/yz"/imm32
2333     68/push  _test-input-stream/imm32
2334     # . . call
2335     e8/call  write/disp32
2336     # . . discard args
2337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2338     # precondition: line->read == 0
2339     # . . push args
2340     68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
2341     68/push  0/imm32
2342     b8/copy-to-EAX  _test-input-stream/imm32
2343     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2344     # . . call
2345     e8/call  check-ints-equal/disp32
2346     # . . discard args
2347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2348     # skip-string(_test-input-stream)
2349     # . . push args
2350     68/push  _test-input-stream/imm32
2351     # . . call
2352     e8/call  skip-string/disp32
2353     # . . discard args
2354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2355     # check-ints-equal(line->read, 6, msg)
2356     # . . push args
2357     68/push  "F - test-skip-string-ignores-escapes"/imm32
2358     68/push  6/imm32
2359     b8/copy-to-EAX  _test-input-stream/imm32
2360     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2361     # . . call
2362     e8/call  check-ints-equal/disp32
2363     # . . discard args
2364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2365     # . epilog
2366     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2367     5d/pop-to-EBP
2368     c3/return
2369 
2370 test-skip-string-works-from-mid-stream:
2371     # . prolog
2372     55/push-EBP
2373     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2374     # setup
2375     # . clear-stream(_test-input-stream)
2376     # . . push args
2377     68/push  _test-input-stream/imm32
2378     # . . call
2379     e8/call  clear-stream/disp32
2380     # . . discard args
2381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2382     # . write(_test-input-stream, "0 \"a\\\"b\"/yz")
2383     # .                   indices:  01 2 34 56
2384     # . . push args
2385     68/push  "0 \"a\\\"b\"/yz"/imm32
2386     68/push  _test-input-stream/imm32
2387     # . . call
2388     e8/call  write/disp32
2389     # . . discard args
2390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2391     # precondition: line->read == 2
2392     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         2/imm32           # copy to *(EAX+4)
2393     # skip-string(_test-input-stream)
2394     # . . push args
2395     68/push  _test-input-stream/imm32
2396     # . . call
2397     e8/call  skip-string/disp32
2398     # . . discard args
2399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2400     # check-ints-equal(line->read, 8, msg)
2401     # . . push args
2402     68/push  "F - test-skip-string-works-from-mid-stream"/imm32
2403     68/push  8/imm32
2404     b8/copy-to-EAX  _test-input-stream/imm32
2405     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
2406     # . . call
2407     e8/call  check-ints-equal/disp32
2408     # . . discard args
2409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2410     # . epilog
2411     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2412     5d/pop-to-EBP
2413     c3/return
2414 
2415 skip-string-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/EAX
2416     # . prolog
2417     55/push-EBP
2418     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2419     # . save registers
2420     51/push-ECX
2421     52/push-EDX
2422     53/push-EBX
2423     # ECX = curr
2424     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2425     # EDX = end
2426     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
2427     # EAX = 0
2428     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2429     # skip initial dquote
2430     41/increment-ECX
2431 $skip-string-in-slice:loop:
2432     # if (curr >= end) return curr
2433     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
2434     73/jump-if-greater-unsigned-or-equal  $skip-string-in-slice:return-curr/disp8
2435     # AL = *curr
2436     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
2437 $skip-string-in-slice:dquote:
2438     # if (EAX == '"') break
2439     3d/compare-EAX-and  0x22/imm32/double-quote
2440     74/jump-if-equal  $skip-string-in-slice:break/disp8
2441 $skip-string-in-slice:check-for-escape:
2442     # if (EAX == '\') escape next char
2443     3d/compare-EAX-and  0x5c/imm32/backslash
2444     75/jump-if-not-equal  $skip-string-in-slice:continue/disp8
2445 $skip-string-in-slice:escape:
2446     41/increment-ECX
2447 $skip-string-in-slice:continue:
2448     # ++curr
2449     41/increment-ECX
2450     eb/jump  $skip-string-in-slice:loop/disp8
2451 $skip-string-in-slice:break:
2452     # skip final dquote
2453     41/increment-ECX
2454 $skip-string-in-slice:return-curr:
2455     # return curr
2456     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
2457 $skip-string-in-slice:end:
2458     # . restore registers
2459     5b/pop-to-EBX
2460     5a/pop-to-EDX
2461     59/pop-to-ECX
2462     # . epilog
2463     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2464     5d/pop-to-EBP
2465     c3/return
2466 
2467 test-skip-string-in-slice:
2468     # . prolog
2469     55/push-EBP
2470     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2471     # setup: (EAX..ECX) = "\"abc\" def"
2472     b8/copy-to-EAX  "\"abc\" def"/imm32
2473     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2474     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
2475     05/add-to-EAX  4/imm32
2476     # EAX = skip-string-in-slice(EAX, ECX)
2477     # . . push args
2478     51/push-ECX
2479     50/push-EAX
2480     # . . call
2481     e8/call  skip-string-in-slice/disp32
2482     # . . discard args
2483     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2484     # check-ints-equal(ECX-EAX, 4, msg)  # number of chars remaining after the string literal
2485     # . . push args
2486     68/push  "F - test-skip-string-in-slice"/imm32
2487     68/push  4/imm32
2488     # . . push ECX-EAX
2489     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2490     51/push-ECX
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     # . epilog
2496     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2497     5d/pop-to-EBP
2498     c3/return
2499 
2500 test-skip-string-in-slice-ignores-spaces:
2501     # . prolog
2502     55/push-EBP
2503     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2504     # setup: (EAX..ECX) = "\"a b\"/yz"
2505     b8/copy-to-EAX  "\"a b\"/yz"/imm32
2506     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2507     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
2508     05/add-to-EAX  4/imm32
2509     # EAX = skip-string-in-slice(EAX, ECX)
2510     # . . push args
2511     51/push-ECX
2512     50/push-EAX
2513     # . . call
2514     e8/call  skip-string-in-slice/disp32
2515     # . . discard args
2516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2517     # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
2518     # . . push args
2519     68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
2520     68/push  3/imm32
2521     # . . push ECX-EAX
2522     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2523     51/push-ECX
2524     # . . call
2525     e8/call  check-ints-equal/disp32
2526     # . . discard args
2527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2528     # . epilog
2529     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2530     5d/pop-to-EBP
2531     c3/return
2532 
2533 test-skip-string-in-slice-ignores-escapes:
2534     # . prolog
2535     55/push-EBP
2536     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2537     # setup: (EAX..ECX) = "\"a\\\"b\"/yz"
2538     b8/copy-to-EAX  "\"a\\\"b\"/yz"/imm32
2539     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2540     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
2541     05/add-to-EAX  4/imm32
2542     # EAX = skip-string-in-slice(EAX, ECX)
2543     # . . push args
2544     51/push-ECX
2545     50/push-EAX
2546     # . . call
2547     e8/call  skip-string-in-slice/disp32
2548     # . . discard args
2549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2550     # check-ints-equal(ECX-EAX, 3, msg)  # number of chars remaining after the string literal
2551     # . . push args
2552     68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
2553     68/push  3/imm32
2554     # . . push ECX-EAX
2555     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2556     51/push-ECX
2557     # . . call
2558     e8/call  check-ints-equal/disp32
2559     # . . discard args
2560     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2561     # . epilog
2562     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2563     5d/pop-to-EBP
2564     c3/return
2565 
2566 test-skip-string-in-slice-stops-at-end:
2567     # . prolog
2568     55/push-EBP
2569     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2570     # setup: (EAX..ECX) = "\"abc"  # unbalanced dquote
2571     b8/copy-to-EAX  "\"abc"/imm32
2572     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2573     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
2574     05/add-to-EAX  4/imm32
2575     # EAX = skip-string-in-slice(EAX, ECX)
2576     # . . push args
2577     51/push-ECX
2578     50/push-EAX
2579     # . . call
2580     e8/call  skip-string-in-slice/disp32
2581     # . . discard args
2582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2583     # check-ints-equal(ECX-EAX, 0, msg)  # skipped to end of slice
2584     # . . push args
2585     68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
2586     68/push  0/imm32
2587     # . . push ECX-EAX
2588     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
2589     51/push-ECX
2590     # . . call
2591     e8/call  check-ints-equal/disp32
2592     # . . discard args
2593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2594     # . epilog
2595     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2596     5d/pop-to-EBP
2597     c3/return
2598 
2599 string-length-at-start-of-slice:  # curr : (address byte), end : (address byte) -> length/EAX
2600     # . prolog
2601     55/push-EBP
2602     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2603     # . save registers
2604     51/push-ECX
2605     52/push-EDX
2606     53/push-EBX
2607     # ECX = curr
2608     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
2609     # EDX = end
2610     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
2611     # length/EAX = 0
2612     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2613     # EBX = 0
2614     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2615     # skip initial dquote
2616     41/increment-ECX
2617 $string-length-at-start-of-slice:loop:
2618     # if (curr >= end) return length
2619     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
2620     73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
2621     # BL = *curr
2622     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
2623 $string-length-at-start-of-slice:dquote:
2624     # if (EBX == '"') break
2625     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x22/imm32/dquote # compare EBX
2626     74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
2627 $string-length-at-start-of-slice:check-for-escape:
2628     # if (EBX == '\') escape next char
2629     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x5c/imm32/backslash # compare EBX
2630     75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
2631 $string-length-at-start-of-slice:escape:
2632     # increment curr but not result
2633     41/increment-ECX
2634 $string-length-at-start-of-slice:continue:
2635     # ++result
2636     40/increment-EAX
2637     # ++curr
2638     41/increment-ECX
2639     eb/jump  $string-length-at-start-of-slice:loop/disp8
2640 $string-length-at-start-of-slice:end:
2641     # . restore registers
2642     5b/pop-to-EBX
2643     5a/pop-to-EDX
2644     59/pop-to-ECX
2645     # . epilog
2646     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2647     5d/pop-to-EBP
2648     c3/return
2649 
2650 test-string-length-at-start-of-slice:
2651     # . prolog
2652     55/push-EBP
2653     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2654     # setup: (EAX..ECX) = "\"abc\" def"
2655     b8/copy-to-EAX  "\"abc\" def"/imm32
2656     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2657     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
2658     05/add-to-EAX  4/imm32
2659     # EAX = string-length-at-start-of-slice(EAX, ECX)
2660     # . . push args
2661     51/push-ECX
2662     50/push-EAX
2663     # . . call
2664     e8/call  string-length-at-start-of-slice/disp32
2665     # . . discard args
2666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2667     # check-ints-equal(EAX, 3, msg)
2668     # . . push args
2669     68/push  "F - test-string-length-at-start-of-slice"/imm32
2670     68/push  3/imm32
2671     50/push-EAX
2672     # . . call
2673     e8/call  check-ints-equal/disp32
2674     # . . discard args
2675     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2676     # . epilog
2677     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2678     5d/pop-to-EBP
2679     c3/return
2680 
2681 test-string-length-at-start-of-slice-escaped:
2682     # . prolog
2683     55/push-EBP
2684     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2685     # setup: (EAX..ECX) = "\"ab\\c\" def"
2686     b8/copy-to-EAX  "\"ab\\c\" def"/imm32
2687     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2688     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
2689     05/add-to-EAX  4/imm32
2690     # EAX = string-length-at-start-of-slice(EAX, ECX)
2691     # . . push args
2692     51/push-ECX
2693     50/push-EAX
2694     # . . call
2695     e8/call  string-length-at-start-of-slice/disp32
2696     # . . discard args
2697     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2698     # check-ints-equal(EAX, 3, msg)
2699     # . . push args
2700     68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
2701     68/push  3/imm32
2702     50/push-EAX
2703     # . . call
2704     e8/call  check-ints-equal/disp32
2705     # . . discard args
2706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2707     # . epilog
2708     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2709     5d/pop-to-EBP
2710     c3/return
2711 
2712 == data
2713 
2714 Next-string-literal:  # tracks the next auto-generated variable name
2715   1/imm32
2716 
2717 # length-prefixed string containing just a single space
2718 Space:
2719     # size
2720     1/imm32
2721     # data
2722     20/space
2723 
2724 # length-prefixed string containing just a single slash
2725 Slash:
2726     # size
2727     1/imm32
2728     # data
2729     2f/slash
2730 
2731 _test-slice-abc:
2732   22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
2733   2f/slash 64/d
2734 _test-slice-abc-limit:
2735 
2736 _test-slice-a-space-b:
2737   22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
2738 _test-slice-a-space-b-limit:
2739 
2740 _test-slice-a-dquote-b:
2741   22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
2742 _test-slice-a-dquote-b-limit:
2743 
2744 _test-slice-a-newline-b:
2745   22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote  # "a\nb"
2746 _test-slice-a-newline-b-limit:
2747 
2748 # "abc/def"/ghi
2749 _test-slice-literal-string:
2750   22/dquote
2751   61/a 62/b 63/c  # abc
2752   2f/slash 64/d 65/e 66/f  # /def
2753   22/dquote
2754   2f/slash 67/g 68/h 69/i  # /ghi
2755 _test-slice-literal-string-with-limit:
2756 
2757 # . . vim:nowrap:textwidth=0