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:
   5 #   $ ./subx translate init.linux 0*.subx apps/subx-params.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:  # run tests if necessary, convert stdin if not
  23     # . prologue
  24     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  25 
  26     # initialize heap
  27     # . Heap = new-segment(Heap-size)
  28     # . . push args
  29     68/push  Heap/imm32
  30     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  31     # . . call
  32     e8/call  new-segment/disp32
  33     # . . discard args
  34     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  35 
  36     # - if argc > 1 and argv[1] == "test", then return run-tests()
  37     # if (argc <= 1) goto interactive
  38     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  39     7e/jump-if-lesser-or-equal  $subx-dquotes-main:interactive/disp8
  40     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  41     # . eax = kernel-string-equal?(argv[1], "test")
  42     # . . push args
  43     68/push  "test"/imm32
  44     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  45     # . . call
  46     e8/call  kernel-string-equal?/disp32
  47     # . . discard args
  48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  49     # . if (eax == 0) goto interactive
  50     3d/compare-eax-and  0/imm32
  51     74/jump-if-equal  $subx-dquotes-main:interactive/disp8
  52     # run-tests()
  53     e8/call  run-tests/disp32
  54     # syscall(exit, *Num-test-failures)
  55     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  56     eb/jump  $subx-dquotes-main:end/disp8
  57 $subx-dquotes-main:interactive:
  58     # - otherwise convert stdin
  59     # var ed/eax : exit-descriptor
  60     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
  61     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
  62     # configure ed to really exit()
  63     # . ed->target = 0
  64     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
  65     # subx-dquotes(Stdin, Stdout, Stderr, ed)
  66     # . . push args
  67     50/push-eax/ed
  68     68/push  Stderr/imm32
  69     68/push  Stdout/imm32
  70     68/push  Stdin/imm32
  71     # . . call
  72     e8/call  subx-dquotes/disp32
  73     # . . discard args
  74     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
  75     # syscall(exit, 0)
  76     bb/copy-to-ebx  0/imm32
  77 $subx-dquotes-main:end:
  78     b8/copy-to-eax  1/imm32/exit
  79     cd/syscall  0x80/imm8
  80 
  81 # conceptual hierarchy within a line:
  82 #   line = words separated by ' ', maybe followed by comment starting with '#'
  83 #   word = datum until '/', then 0 or more metadata separated by '/'
  84 
  85 subx-dquotes:  # in : (address buffered-file), out : (address buffered-file)
  86     # pseudocode:
  87     #   var line : (stream byte 512)
  88     #   var new-data-segment : (handle stream byte) = new-stream(Heap, Segment-size, 1)
  89     #   write(new-data-segment, "== data\n")
  90     #   while true
  91     #     clear-stream(line)
  92     #     read-line-buffered(in, line)
  93     #     if (line->write == 0) break               # end of file
  94     #     while true
  95     #       var word-slice = next-word-or-string(line)
  96     #       if slice-empty?(word-slice)             # end of line
  97     #         break
  98     #       if slice-starts-with?(word-slice, "#")  # comment
  99     #         continue
 100     #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
 101     #         process-string-literal(word-slice, out, new-data-segment)
 102     #       else
 103     #         write-slice-buffered(out, word-slice)
 104     #       write(out, " ")
 105     #     write(out, "\n\n")
 106     #   write-stream-data(out, new-data-segment)
 107     #   flush(out)
 108     #
 109     # . prologue
 110     55/push-ebp
 111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 112     # . save registers
 113     50/push-eax
 114     51/push-ecx
 115     52/push-edx
 116     53/push-ebx
 117     56/push-esi
 118     57/push-edi
 119     # var line/ecx : (address stream byte) = stream(512)
 120     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 121     68/push  0x200/imm32/length
 122     68/push  0/imm32/read
 123     68/push  0/imm32/write
 124     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 125     # var word-slice/edx = {0, 0}
 126     68/push  0/imm32/end
 127     68/push  0/imm32/start
 128     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 129     # new-data-segment/edi = new-stream(Heap, Segment-size, 1)
 130     # . eax = new-stream(Heap, Segment-size, 1)
 131     # . . push args
 132     68/push  1/imm32
 133     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 134     68/push  Heap/imm32
 135     # . . call
 136     e8/call  new-stream/disp32
 137     # . . discard args
 138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 139     # . edi = eax
 140     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
 141     # write(new-data-segment, "== data\n")
 142     # . . push args
 143     68/push  "== data\n"/imm32
 144     57/push-edi
 145     # . . call
 146     e8/call  write/disp32
 147     # . . discard args
 148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 149 $subx-dquotes:line-loop:
 150     # clear-stream(line)
 151     # . . push args
 152     51/push-ecx
 153     # . . call
 154     e8/call  clear-stream/disp32
 155     # . . discard args
 156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 157     # read-line-buffered(in, line)
 158     # . . push args
 159     51/push-ecx
 160     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 161     # . . call
 162     e8/call  read-line-buffered/disp32
 163     # . . discard args
 164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 165 $subx-dquotes:check0:
 166     # if (line->write == 0) break
 167     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 168     0f 84/jump-if-equal  $subx-dquotes:break/disp32
 169 $subx-dquotes:word-loop:
 170     # next-word-or-string(line, word-slice)
 171     # . . push args
 172     52/push-edx
 173     51/push-ecx
 174     # . . call
 175     e8/call  next-word-or-string/disp32
 176     # . . discard args
 177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 178 $subx-dquotes:check1:
 179     # if (slice-empty?(word-slice)) break
 180     # . eax = slice-empty?(word-slice)
 181     # . . push args
 182     52/push-edx
 183     # . . call
 184     e8/call  slice-empty?/disp32
 185     # . . discard args
 186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 187     # . if (eax != 0) break
 188     3d/compare-eax-and  0/imm32
 189     0f 85/jump-if-not-equal  $subx-dquotes:next-line/disp32
 190 $subx-dquotes:check-for-comment:
 191     # if (slice-starts-with?(word-slice, "#")) continue
 192     # . start/esi = word-slice->start
 193     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
 194     # . c/eax = *start
 195     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 196     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
 197     # . if (eax == '#') continue
 198     3d/compare-eax-and  0x23/imm32/hash
 199     74/jump-if-equal  $subx-dquotes:word-loop/disp8
 200 $subx-dquotes:check-for-string-literal:
 201     # if (slice-starts-with?(word-slice, '"')) continue
 202     3d/compare-eax-and  0x22/imm32/dquote
 203     75/jump-if-not-equal  $subx-dquotes:regular-word/disp8
 204 $subx-dquotes:string-literal:
 205     # process-string-literal(word-slice, out, new-data-segment)
 206     # . . push args
 207     57/push-edi
 208     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 209     52/push-edx
 210     # . . call
 211     e8/call  process-string-literal/disp32
 212     # . . discard args
 213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 214     # continue
 215     eb/jump  $subx-dquotes:next-word/disp8
 216 $subx-dquotes:regular-word:
 217     # write-slice-buffered(out, word-slice)
 218     # . . push args
 219     52/push-edx
 220     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 221     # . . call
 222     e8/call  write-slice-buffered/disp32
 223     # . . discard args
 224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 225     # fall through
 226 $subx-dquotes:next-word:
 227     # write-buffered(out, " ")
 228     # . . push args
 229     68/push  Space/imm32
 230     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 231     # . . call
 232     e8/call  write-buffered/disp32
 233     # . . discard args
 234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 235     # loop
 236     eb/jump  $subx-dquotes:word-loop/disp8
 237 $subx-dquotes:next-line:
 238     # write-buffered(out, "\n")
 239     # . . push args
 240     68/push  Newline/imm32
 241     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 242     # . . call
 243     e8/call  write-buffered/disp32
 244     # . . discard args
 245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 246     # loop
 247     e9/jump  $subx-dquotes:line-loop/disp32
 248 $subx-dquotes:break:
 249     # write-stream-data(out, new-data-segment)
 250     # . . push args
 251     57/push-edi
 252     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 253     # . . call
 254     e8/call  write-stream-data/disp32
 255     # . . discard args
 256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 257     # flush(out)
 258     # . . push args
 259     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 260     # . . call
 261     e8/call  flush/disp32
 262     # . . discard args
 263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 264 $subx-dquotes:end:
 265     # . reclaim locals
 266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 267     # . restore registers
 268     5f/pop-to-edi
 269     5e/pop-to-esi
 270     5b/pop-to-ebx
 271     5a/pop-to-edx
 272     59/pop-to-ecx
 273     58/pop-to-eax
 274     # . epilogue
 275     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 276     5d/pop-to-ebp
 277     c3/return
 278 
 279 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
 280 # label, and write the new label out to 'out'.
 281 process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
 282     # pseudocode:
 283     #   print(out-segment, "_string#{Next-string-literal}:\n")
 284     #   emit-string-literal-data(out-segment, string-literal)
 285     #   print(out, "_string#{Next-string-literal}")
 286     #   emit-metadata(out, string-literal)
 287     #   ++ *Next-string-literal
 288     #
 289     # . prologue
 290     55/push-ebp
 291     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 292     # . save registers
 293     51/push-ecx
 294     # var int32-stream/ecx = stream(10)  # number of decimal digits a 32-bit number can have
 295     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa/imm32         # subtract from esp
 296     68/push  0xa/imm32/decimal-digits-in-32bit-number
 297     68/push  0/imm32/read
 298     68/push  0/imm32/write
 299     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 300     # print(out-segment, "_string#{Next-string-literal}:\n")
 301     # . write(out-segment, "_string")
 302     # . . push args
 303     68/push  "_string"/imm32
 304     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 305     # . . call
 306     e8/call  write/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 309     # . print-int32-decimal(out-segment, *Next-string-literal)
 310     # . . push args
 311     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 312     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 313     # . . call
 314     e8/call  print-int32-decimal/disp32
 315     # . . discard args
 316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 317     # . write(out-segment, ":\n")
 318     # . . push args
 319     68/push  ":\n"/imm32
 320     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 321     # . . call
 322     e8/call  write/disp32
 323     # . . discard args
 324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 325     # emit-string-literal-data(out-segment, string-literal)
 326     # . . push args
 327     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 328     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 329     # . . call
 330     e8/call  emit-string-literal-data/disp32
 331     # . . discard args
 332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 333     # write(out-segment, "\n")
 334     # . . push args
 335     68/push  Newline/imm32
 336     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 337     # . . call
 338     e8/call  write/disp32
 339     # . . discard args
 340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 341     # print(out, "_string#{Next-string-literal}")
 342     # . write-buffered(out, "_string")
 343     # . . push args
 344     68/push  "_string"/imm32
 345     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 346     # . . call
 347     e8/call  write-buffered/disp32
 348     # . . discard args
 349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 350     # . print-int32-decimal(int32-stream, *Next-string-literal)
 351     # . . push args
 352     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # push *Next-string-literal
 353     51/push-ecx
 354     # . . call
 355     e8/call  print-int32-decimal/disp32
 356     # . . discard args
 357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 358     # . write-stream-data(out, int32-stream)
 359     # . . push args
 360     51/push-ecx
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 362     # . . call
 363     e8/call  write-stream-data/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 366     # emit-metadata(out, string-literal)
 367     # . . push args
 368     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 369     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 370     # . . call
 371     e8/call  emit-metadata/disp32
 372     # . . discard args
 373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 374     # ++ *Next-string-literal
 375     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
 376 $process-string-literal:end:
 377     # . reclaim locals
 378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x16/imm32        # add to esp
 379     # . restore registers
 380     59/pop-to-ecx
 381     # . epilogue
 382     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 383     5d/pop-to-ebp
 384     c3/return
 385 
 386 test-subx-dquotes-is-idempotent-by-default:
 387     # . prologue
 388     55/push-ebp
 389     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 390     # setup
 391     # . clear-stream(_test-input-stream)
 392     # . . push args
 393     68/push  _test-input-stream/imm32
 394     # . . call
 395     e8/call  clear-stream/disp32
 396     # . . discard args
 397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 398     # . clear-stream(_test-input-buffered-file->buffer)
 399     # . . push args
 400     68/push  _test-input-buffered-file->buffer/imm32
 401     # . . call
 402     e8/call  clear-stream/disp32
 403     # . . discard args
 404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 405     # . clear-stream(_test-output-stream)
 406     # . . push args
 407     68/push  _test-output-stream/imm32
 408     # . . call
 409     e8/call  clear-stream/disp32
 410     # . . discard args
 411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 412     # . clear-stream(_test-output-buffered-file->buffer)
 413     # . . push args
 414     68/push  _test-output-buffered-file->buffer/imm32
 415     # . . call
 416     e8/call  clear-stream/disp32
 417     # . . discard args
 418     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 419     # initialize input (meta comments in parens)
 420     #   # comment 1
 421     #     # comment 2 indented
 422     #   == code 0x1  (new segment)
 423     #   # comment 3 inside a segment
 424     #   1
 425     #                         (empty line)
 426     #   2 3 # comment 4 inline with other contents
 427     #   == data 0x2  (new segment)
 428     #   4 5/imm32
 429     # . write(_test-input-stream, "# comment 1\n")
 430     # . . push args
 431     68/push  "# comment 1\n"/imm32
 432     68/push  _test-input-stream/imm32
 433     # . . call
 434     e8/call  write/disp32
 435     # . . discard args
 436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 437     # . write(_test-input-stream, "  # comment 2 indented\n")
 438     # . . push args
 439     68/push  "  # comment 2 indented\n"/imm32
 440     68/push  _test-input-stream/imm32
 441     # . . call
 442     e8/call  write/disp32
 443     # . . discard args
 444     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 445     # . write(_test-input-stream, "== code 0x1\n")
 446     # . . push args
 447     68/push  "== code 0x1\n"/imm32
 448     68/push  _test-input-stream/imm32
 449     # . . call
 450     e8/call  write/disp32
 451     # . . discard args
 452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 453     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 454     # . . push args
 455     68/push  "# comment 3 inside a segment\n"/imm32
 456     68/push  _test-input-stream/imm32
 457     # . . call
 458     e8/call  write/disp32
 459     # . . discard args
 460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 461     # . write(_test-input-stream, "1\n")
 462     # . . push args
 463     68/push  "1\n"/imm32
 464     68/push  _test-input-stream/imm32
 465     # . . call
 466     e8/call  write/disp32
 467     # . . discard args
 468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 469     # . write(_test-input-stream, "\n")  # empty line
 470     # . . push args
 471     68/push  Newline/imm32
 472     68/push  _test-input-stream/imm32
 473     # . . call
 474     e8/call  write/disp32
 475     # . . discard args
 476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 477     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 478     # . . push args
 479     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 480     68/push  _test-input-stream/imm32
 481     # . . call
 482     e8/call  write/disp32
 483     # . . discard args
 484     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 485     # . write(_test-input-stream, "== data 0x2\n")
 486     # . . push args
 487     68/push  "== data 0x2\n"/imm32
 488     68/push  _test-input-stream/imm32
 489     # . . call
 490     e8/call  write/disp32
 491     # . . discard args
 492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 493     # . write(_test-input-stream, "4 5/imm32\n")
 494     # . . push args
 495     68/push  "4 5/imm32\n"/imm32
 496     68/push  _test-input-stream/imm32
 497     # . . call
 498     e8/call  write/disp32
 499     # . . discard args
 500     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 501     # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
 502     # . . push args
 503     68/push  _test-output-buffered-file/imm32
 504     68/push  _test-input-buffered-file/imm32
 505     # . . call
 506     e8/call  subx-dquotes/disp32
 507     # . . discard args
 508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 509     # . flush(_test-output-buffered-file)
 510     # . . push args
 511     68/push  _test-output-buffered-file/imm32
 512     # . . call
 513     e8/call  flush/disp32
 514     # . . discard args
 515     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 516     # check output
 517     #     (comment dropped for now)
 518     #     (comment dropped for now)
 519     #   == code 0x1
 520     #     (comment dropped for now)
 521     #   1
 522     #     (comment dropped for now)
 523     #   2 3
 524     #   == data 0x2
 525     #   4 5/imm32
 526     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 527 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 553     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 554     # . . push args
 555     68/push  "F - test-subx-dquotes-is-idempotent-by-default/0"/imm32
 556     68/push  ""/imm32
 557     68/push  _test-output-stream/imm32
 558     # . . call
 559     e8/call  check-next-stream-line-equal/disp32
 560     # . . discard args
 561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 562     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 563     # . . push args
 564     68/push  "F - test-subx-dquotes-is-idempotent-by-default/1"/imm32
 565     68/push  ""/imm32
 566     68/push  _test-output-stream/imm32
 567     # . . call
 568     e8/call  check-next-stream-line-equal/disp32
 569     # . . discard args
 570     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 571     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 572     # . . push args
 573     68/push  "F - test-subx-dquotes-is-idempotent-by-default/2"/imm32
 574     68/push  "== code 0x1 "/imm32
 575     68/push  _test-output-stream/imm32
 576     # . . call
 577     e8/call  check-next-stream-line-equal/disp32
 578     # . . discard args
 579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 580     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 581     # . . push args
 582     68/push  "F - test-subx-dquotes-is-idempotent-by-default/3"/imm32
 583     68/push  ""/imm32
 584     68/push  _test-output-stream/imm32
 585     # . . call
 586     e8/call  check-next-stream-line-equal/disp32
 587     # . . discard args
 588     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 589     # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
 590     # . . push args
 591     68/push  "F - test-subx-dquotes-is-idempotent-by-default/4"/imm32
 592     68/push  "1 "/imm32
 593     68/push  _test-output-stream/imm32
 594     # . . call
 595     e8/call  check-next-stream-line-equal/disp32
 596     # . . discard args
 597     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 598     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 599     # . . push args
 600     68/push  "F - test-subx-dquotes-is-idempotent-by-default/5"/imm32
 601     68/push  ""/imm32
 602     68/push  _test-output-stream/imm32
 603     # . . call
 604     e8/call  check-next-stream-line-equal/disp32
 605     # . . discard args
 606     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 607     # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
 608     # . . push args
 609     68/push  "F - test-subx-dquotes-is-idempotent-by-default/6"/imm32
 610     68/push  "2 3 "/imm32
 611     68/push  _test-output-stream/imm32
 612     # . . call
 613     e8/call  check-next-stream-line-equal/disp32
 614     # . . discard args
 615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 616     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
 617     # . . push args
 618     68/push  "F - test-subx-dquotes-is-idempotent-by-default/7"/imm32
 619     68/push  "== data 0x2 "/imm32
 620     68/push  _test-output-stream/imm32
 621     # . . call
 622     e8/call  check-next-stream-line-equal/disp32
 623     # . . discard args
 624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 625     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
 626     # . . push args
 627     68/push  "F - test-subx-dquotes-is-idempotent-by-default/8"/imm32
 628     68/push  "4 5/imm32 "/imm32
 629     68/push  _test-output-stream/imm32
 630     # . . call
 631     e8/call  check-next-stream-line-equal/disp32
 632     # . . discard args
 633     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 634     # . epilogue
 635     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 636     5d/pop-to-ebp
 637     c3/return
 638 
 639 test-subx-dquotes-processes-string-literals:
 640     # . prologue
 641     55/push-ebp
 642     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 643     # setup
 644     # . clear-stream(_test-input-stream)
 645     # . . push args
 646     68/push  _test-input-stream/imm32
 647     # . . call
 648     e8/call  clear-stream/disp32
 649     # . . discard args
 650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 651     # . clear-stream(_test-input-buffered-file->buffer)
 652     # . . push args
 653     68/push  _test-input-buffered-file->buffer/imm32
 654     # . . call
 655     e8/call  clear-stream/disp32
 656     # . . discard args
 657     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 658     # . clear-stream(_test-output-stream)
 659     # . . push args
 660     68/push  _test-output-stream/imm32
 661     # . . call
 662     e8/call  clear-stream/disp32
 663     # . . discard args
 664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 665     # . clear-stream(_test-output-buffered-file->buffer)
 666     # . . push args
 667     68/push  _test-output-buffered-file->buffer/imm32
 668     # . . call
 669     e8/call  clear-stream/disp32
 670     # . . discard args
 671     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 672     # initialize input (meta comments in parens)
 673     #   == code  (new segment)
 674     #   1 "a"/x
 675     #   2 "bc"/y
 676     68/push  "== code 0x1\n"/imm32
 677     68/push  _test-input-stream/imm32
 678     # . . call
 679     e8/call  write/disp32
 680     # . . discard args
 681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 682     # . write(_test-input-stream, "1 \"a\"/x\n")
 683     # . . push args
 684     68/push  "1 \"a\"/x\n"/imm32
 685     68/push  _test-input-stream/imm32
 686     # . . call
 687     e8/call  write/disp32
 688     # . . discard args
 689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 690     # . write(_test-input-stream, "2 \"bc\"/y\n")
 691     # . . push args
 692     68/push  "2 \"bc\"/y\n"/imm32
 693     68/push  _test-input-stream/imm32
 694     # . . call
 695     e8/call  write/disp32
 696     # . . discard args
 697     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 698     # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
 699     # . . push args
 700     68/push  _test-output-buffered-file/imm32
 701     68/push  _test-input-buffered-file/imm32
 702     # . . call
 703     e8/call  subx-dquotes/disp32
 704     # . . discard args
 705     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 706     # . flush(_test-output-buffered-file)
 707     # . . push args
 708     68/push  _test-output-buffered-file/imm32
 709     # . . call
 710     e8/call  flush/disp32
 711     # . . discard args
 712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 713     # check output
 714     #   == code 0x1
 715     #   1 _string1/x
 716     #   2 _string2/y
 717     #   == data
 718     #   _string1:
 719     #   1/imm32 61/a
 720     #   _string2:
 721     #   2/imm32 62/b 63/c
 722     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 723     #
 724     # Open question: how to make this check more robust.
 725     # We don't actually care what the auto-generated string variables are
 726     # called. We just want to make sure instructions using string literals
 727     # switch to a string variable with the right value.
 728     # (Modifying string literals completely off the radar for now.)
 729 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 762     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
 763     # . . push args
 764     68/push  "F - test-subx-dquotes-processes-string-literals/0"/imm32
 765     68/push  "== code 0x1 "/imm32
 766     68/push  _test-output-stream/imm32
 767     # . . call
 768     e8/call  check-next-stream-line-equal/disp32
 769     # . . discard args
 770     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 771     # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
 772     # . . push args
 773     68/push  "F - test-subx-dquotes-processes-string-literals/1"/imm32
 774     68/push  "1 _string1/x "/imm32
 775     68/push  _test-output-stream/imm32
 776     # . . call
 777     e8/call  check-next-stream-line-equal/disp32
 778     # . . discard args
 779     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 780     # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
 781     # . . push args
 782     68/push  "F - test-subx-dquotes-processes-string-literals/2"/imm32
 783     68/push  "2 _string2/y "/imm32
 784     68/push  _test-output-stream/imm32
 785     # . . call
 786     e8/call  check-next-stream-line-equal/disp32
 787     # . . discard args
 788     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 789     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
 790     # . . push args
 791     68/push  "F - test-subx-dquotes-processes-string-literals/3"/imm32
 792     68/push  "== data"/imm32
 793     68/push  _test-output-stream/imm32
 794     # . . call
 795     e8/call  check-next-stream-line-equal/disp32
 796     # . . discard args
 797     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 798     # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
 799     # . . push args
 800     68/push  "F - test-subx-dquotes-processes-string-literals/4"/imm32
 801     68/push  "_string1:"/imm32
 802     68/push  _test-output-stream/imm32
 803     # . . call
 804     e8/call  check-next-stream-line-equal/disp32
 805     # . . discard args
 806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 807     # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
 808     # . . push args
 809     68/push  "F - test-subx-dquotes-processes-string-literals/5"/imm32
 810     68/push  "0x00000001/imm32 61/a "/imm32
 811     68/push  _test-output-stream/imm32
 812     # . . call
 813     e8/call  check-next-stream-line-equal/disp32
 814     # . . discard args
 815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 816     # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
 817     # . . push args
 818     68/push  "F - test-subx-dquotes-processes-string-literals/6"/imm32
 819     68/push  "_string2:"/imm32
 820     68/push  _test-output-stream/imm32
 821     # . . call
 822     e8/call  check-next-stream-line-equal/disp32
 823     # . . discard args
 824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 825     # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
 826     # . . push args
 827     68/push  "F - test-subx-dquotes-processes-string-literals/7"/imm32
 828     68/push  "0x00000002/imm32 62/b 63/c "/imm32
 829     68/push  _test-output-stream/imm32
 830     # . . call
 831     e8/call  check-next-stream-line-equal/disp32
 832     # . . discard args
 833     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 834     # . epilogue
 835     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 836     5d/pop-to-ebp
 837     c3/return
 838 
 839 # generate the data segment contents byte by byte for a given slice
 840 emit-string-literal-data:  # out : (address stream), word : (address slice)
 841     # pseudocode
 842     #   len = string-length-at-start-of-slice(word->start, word->end)
 843     #   print(out, "#{len}/imm32 ")
 844     #   curr = word->start
 845     #   ++curr  # skip '"'
 846     #   idx = 0
 847     #   while true
 848     #     if (curr >= word->end) break
 849     #     c = *curr
 850     #     if (c == '"') break
 851     #     if (c == '\') {
 852     #       ++curr
 853     #       c = *curr
 854     #       if (c == 'n')
 855     #         c = newline
 856     #     }
 857     #     append-byte-hex(out, c)
 858     #     if c is alphanumeric:
 859     #       write(out, "/")
 860     #       append-byte(out, c)
 861     #     write(out, " ")
 862     #     ++curr
 863     #     ++idx
 864     #     if idx >= 0x40
 865     #       idx = 0
 866     #       write(out, "\n")
 867     #
 868     # . prologue
 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     53/push-ebx
 876     56/push-esi
 877     # esi = word
 878     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 879     # idx/ebx = 0
 880     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 881     # curr/edx = word->start
 882     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
 883     # max/esi = word->end
 884     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
 885 $emit-string-literal-data:emit-length:
 886     # len/eax = string-length-at-start-of-slice(word->start, word->end)
 887     # . . push args
 888     56/push-esi
 889     52/push-edx
 890     # . . call
 891     e8/call  string-length-at-start-of-slice/disp32
 892     # . . discard args
 893     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 894     # print(out, "#{len}/imm32 ")
 895     # . print-int32(out, len)
 896     # . . push args
 897     50/push-eax
 898     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 899     # . . call
 900     e8/call  print-int32/disp32
 901     # . . discard args
 902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 903     # . write(out, "/imm32 ")
 904     # . . push args
 905     68/push  "/imm32 "/imm32
 906     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 907     # . . call
 908     e8/call  write/disp32
 909     # . . discard args
 910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 911 $emit-string-literal-data:loop-init:
 912     # ++curr  # skip initial '"'
 913     42/increment-edx
 914     # c/ecx = 0
 915     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 916 $emit-string-literal-data:loop:
 917     # if (curr >= max) break
 918     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # compare edx with esi
 919     0f 83/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp32
 920     # CL = *curr
 921     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
 922     # if (c == '"') break
 923     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x22/imm32/dquote # compare ecx
 924     0f 84/jump-if-equal  $emit-string-literal-data:end/disp32
 925     # if (c != '\') goto emit
 926     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x5c/imm32/backslash  # compare ecx
 927     75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
 928     # ++curr
 929     42/increment-edx
 930     # if (curr >= max) break
 931     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # compare edx with esi
 932     0f 83/jump-if-greater-or-equal-unsigned  $emit-string-literal-data:end/disp32
 933     # c = *curr
 934     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
 935     # if (c == 'n') c = newline
 936     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x6e/imm32/n      # compare ecx
 937     75/jump-if-not-equal  $emit-string-literal-data:emit/disp8
 938     b9/copy-to-ecx  0x0a/imm32/newline
 939 $emit-string-literal-data:emit:
 940     # append-byte-hex(out, CL)
 941     # . . push args
 942     51/push-ecx
 943     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 944     # . . call
 945     e8/call  append-byte-hex/disp32
 946     # . . discard args
 947     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 948     # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}")
 949     # . eax = is-alphanumeric?(CL)
 950     # . . push args
 951     51/push-ecx
 952     # . . call
 953     e8/call  is-alphanumeric?/disp32
 954     # . . discard args
 955     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 956     # . if (eax == 0) goto char-done
 957     3d/compare-eax-and  0/imm32
 958     74/jump-if-equal  $emit-string-literal-data:char-done/disp8
 959     # . write(out, "/")
 960     # . . push args
 961     68/push  Slash/imm32
 962     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 963     # . . call
 964     e8/call  write/disp32
 965     # . . discard args
 966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 967     # . append-byte(out, *curr)
 968     # . . push args
 969     51/push-ecx
 970     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 971     # . . call
 972     e8/call  append-byte/disp32
 973     # . . discard args
 974     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 975 $emit-string-literal-data:char-done:
 976     # write(out, " ")
 977     # . . push args
 978     68/push  Space/imm32
 979     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 980     # . . call
 981     e8/call  write/disp32
 982     # . . discard args
 983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 984     # ++curr
 985     42/increment-edx
 986     # ++ idx
 987     43/increment-ebx
 988     # if (idx < 0x40) continue
 989     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x40/imm32        # compare ebx
 990     7c/jump-if-lesser  $emit-string-literal-data:next-char/disp8
 991     # idx = 0
 992     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 993     # write(out, "\n")
 994     # . . push args
 995     68/push  Newline/imm32
 996     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 997     # . . call
 998     e8/call  write/disp32
 999     # . . discard args
1000     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1001 $emit-string-literal-data:next-char:
1002     e9/jump $emit-string-literal-data:loop/disp32
1003 $emit-string-literal-data:end:
1004     # . restore registers
1005     5e/pop-to-esi
1006     5b/pop-to-ebx
1007     5a/pop-to-edx
1008     59/pop-to-ecx
1009     58/pop-to-eax
1010     # . epilogue
1011     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1012     5d/pop-to-ebp
1013     c3/return
1014 
1015 is-alphanumeric?:  # c : int -> eax : boolean
1016     # . prologue
1017     55/push-ebp
1018     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1019     # eax = c
1020     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
1021     # if (eax < '0') return false
1022     3d/compare-eax-with  0x30/imm32/0
1023     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1024     # if (eax <= '9') return true
1025     3d/compare-eax-with  0x39/imm32/9
1026     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1027     # if (eax < 'A') return false
1028     3d/compare-eax-with  0x41/imm32/A
1029     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1030     # if (eax <= 'Z') return true
1031     3d/compare-eax-with  0x5a/imm32/Z
1032     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1033     # if (eax < 'a') return false
1034     3d/compare-eax-with  0x61/imm32/a
1035     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
1036     # if (eax <= 'z') return true
1037     3d/compare-eax-with  0x7a/imm32/z
1038     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
1039     # return false
1040 $is-alphanumeric?:false:
1041     b8/copy-to-eax  0/imm32/false
1042     eb/jump  $is-alphanumeric?:end/disp8
1043 $is-alphanumeric?:true:
1044     b8/copy-to-eax  1/imm32/true
1045 $is-alphanumeric?:end:
1046     # . epilogue
1047     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1048     5d/pop-to-ebp
1049     c3/return
1050 
1051 test-emit-string-literal-data:
1052     # . prologue
1053     55/push-ebp
1054     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1055     # setup
1056     # . clear-stream(_test-output-stream)
1057     # . . push args
1058     68/push  _test-output-stream/imm32
1059     # . . call
1060     e8/call  clear-stream/disp32
1061     # . . discard args
1062     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1063     # var slice/ecx = '"abc"/d'
1064     68/push  _test-slice-abc-limit/imm32
1065     68/push  _test-slice-abc/imm32
1066     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1067     # emit-string-literal-data(_test-output-stream, slice)
1068     # . . push args
1069     51/push-ecx
1070     68/push  _test-output-stream/imm32
1071     # . . call
1072     e8/call  emit-string-literal-data/disp32
1073     # . . discard args
1074     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1075 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1101     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg)
1102     # . . push args
1103     68/push  "F - test-emit-string-literal-data"/imm32
1104     68/push  "0x00000003/imm32 61/a 62/b 63/c "/imm32
1105     68/push  _test-output-stream/imm32
1106     # . . call
1107     e8/call  check-stream-equal/disp32
1108     # . . discard args
1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1110     # . epilogue
1111     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1112     5d/pop-to-ebp
1113     c3/return
1114 
1115 test-emit-string-literal-data-empty:
1116     # . prologue
1117     55/push-ebp
1118     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1119     # setup
1120     # . clear-stream(_test-output-stream)
1121     # . . push args
1122     68/push  _test-output-stream/imm32
1123     # . . call
1124     e8/call  clear-stream/disp32
1125     # . . discard args
1126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1127     # var slice/ecx = '""'
1128     68/push  0/imm32/end
1129     68/push  0/imm32/start
1130     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1131     # emit-string-literal-data(_test-output-stream, slice)
1132     # . . push args
1133     51/push-ecx
1134     68/push  _test-output-stream/imm32
1135     # . . call
1136     e8/call  emit-string-literal-data/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1139 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1165     # . check-stream-equal(_test-output-stream, "0/imm32 ", msg)
1166     # . . push args
1167     68/push  "F - test-emit-string-literal-data-empty"/imm32
1168     68/push  "0x00000000/imm32 "/imm32
1169     68/push  _test-output-stream/imm32
1170     # . . call
1171     e8/call  check-stream-equal/disp32
1172     # . . discard args
1173     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1174     # . epilogue
1175     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1176     5d/pop-to-ebp
1177     c3/return
1178 
1179 # just to keep things simple
1180 test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
1181     # . prologue
1182     55/push-ebp
1183     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1184     # setup
1185     # . clear-stream(_test-output-stream)
1186     # . . push args
1187     68/push  _test-output-stream/imm32
1188     # . . call
1189     e8/call  clear-stream/disp32
1190     # . . discard args
1191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1192     # var slice/ecx = '"a b"'
1193     68/push  _test-slice-a-space-b-limit/imm32
1194     68/push  _test-slice-a-space-b/imm32
1195     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1196     # emit-string-literal-data(_test-output-stream, slice)
1197     # . . push args
1198     51/push-ecx
1199     68/push  _test-output-stream/imm32
1200     # . . call
1201     e8/call  emit-string-literal-data/disp32
1202     # . . discard args
1203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1204 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1230     # . 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
1231     # . . push args
1232     68/push  "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32
1233     68/push  "0x00000003/imm32 61/a 20 62/b "/imm32
1234     68/push  _test-output-stream/imm32
1235     # . . call
1236     e8/call  check-stream-equal/disp32
1237     # . . discard args
1238     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1239     # . epilogue
1240     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1241     5d/pop-to-ebp
1242     c3/return
1243 
1244 test-emit-string-literal-data-handles-escape-sequences:
1245     # . prologue
1246     55/push-ebp
1247     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1248     # setup
1249     # . clear-stream(_test-output-stream)
1250     # . . push args
1251     68/push  _test-output-stream/imm32
1252     # . . call
1253     e8/call  clear-stream/disp32
1254     # . . discard args
1255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1256     # var slice/ecx = '"a\"b"'
1257     68/push  _test-slice-a-dquote-b-limit/imm32
1258     68/push  _test-slice-a-dquote-b/imm32
1259     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1260     # emit-string-literal-data(_test-output-stream, slice)
1261     # . . push args
1262     51/push-ecx
1263     68/push  _test-output-stream/imm32
1264     # . . call
1265     e8/call  emit-string-literal-data/disp32
1266     # . . discard args
1267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1268 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1294     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg)
1295     # . . push args
1296     68/push  "F - test-emit-string-literal-data-handles-escape-sequences"/imm32
1297     68/push  "0x00000003/imm32 61/a 22 62/b "/imm32
1298     68/push  _test-output-stream/imm32
1299     # . . call
1300     e8/call  check-stream-equal/disp32
1301     # . . discard args
1302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1303     # . epilogue
1304     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1305     5d/pop-to-ebp
1306     c3/return
1307 
1308 test-emit-string-literal-data-handles-newline-escape:
1309     # . prologue
1310     55/push-ebp
1311     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1312     # setup
1313     # . clear-stream(_test-output-stream)
1314     # . . push args
1315     68/push  _test-output-stream/imm32
1316     # . . call
1317     e8/call  clear-stream/disp32
1318     # . . discard args
1319     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1320     # var slice/ecx = '"a\nb"'
1321     68/push  _test-slice-a-newline-b-limit/imm32
1322     68/push  _test-slice-a-newline-b/imm32
1323     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1324     # emit-string-literal-data(_test-output-stream, slice)
1325     # . . push args
1326     51/push-ecx
1327     68/push  _test-output-stream/imm32
1328     # . . call
1329     e8/call  emit-string-literal-data/disp32
1330     # . . discard args
1331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1332 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1358     # . check-stream-equal(_test-output-stream, "3/imm32 61/a 0a 62/b ", msg)
1359     # . . push args
1360     68/push  "F - test-emit-string-literal-data-handles-newline-escape"/imm32
1361     68/push  "0x00000003/imm32 61/a 0a 62/b "/imm32
1362     68/push  _test-output-stream/imm32
1363     # . . call
1364     e8/call  check-stream-equal/disp32
1365     # . . discard args
1366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1367     # . epilogue
1368     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1369     5d/pop-to-ebp
1370     c3/return
1371 
1372 # emit everything from a word except the initial datum
1373 emit-metadata:  # out : (address buffered-file), word : (address slice)
1374     # pseudocode
1375     #   var slice = {0, word->end}
1376     #   curr = word->start
1377     #   if *curr == '"'
1378     #     curr = skip-string-in-slice(curr, word->end)
1379     #   else
1380     #     while true
1381     #       if curr == word->end
1382     #         return
1383     #       if *curr == '/'
1384     #         break
1385     #       ++curr
1386     #   slice->start = curr
1387     #   write-slice-buffered(out, slice)
1388     #
1389     # . prologue
1390     55/push-ebp
1391     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1392     # . save registers
1393     50/push-eax
1394     51/push-ecx
1395     52/push-edx
1396     53/push-ebx
1397     56/push-esi
1398     # esi = word
1399     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
1400     # curr/ecx = word->start
1401     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
1402     # end/edx = word->end
1403     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
1404     # var slice/ebx = {0, end}
1405     52/push-edx
1406     68/push  0/imm32
1407     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1408     # eax = 0
1409     b8/copy-to-eax  0/imm32
1410 $emit-metadata:check-for-string-literal:
1411     # -  if (*curr == '"') curr = skip-string-in-slice(curr, end)
1412     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1413     3d/compare-eax-and  0x22/imm32/dquote
1414     75/jump-if-not-equal  $emit-metadata:skip-datum-loop/disp8
1415 $emit-metadata:skip-string-literal:
1416     # . eax = skip-string-in-slice(curr, end)
1417     # . . push args
1418     52/push-edx
1419     51/push-ecx
1420     # . . call
1421     e8/call  skip-string-in-slice/disp32
1422     # . . discard args
1423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1424     # . curr = eax
1425     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
1426     eb/jump  $emit-metadata:emit/disp8
1427 $emit-metadata:skip-datum-loop:
1428     # - otherwise scan for '/'
1429     # if (curr == end) return
1430     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx and edx
1431     74/jump-if-equal  $emit-metadata:end/disp8
1432     # if (*curr == '/') break
1433     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1434     3d/compare-eax-and  0x2f/imm32/slash
1435     74/jump-if-equal  $emit-metadata:emit/disp8
1436     # ++curr
1437     41/increment-ecx
1438     eb/jump  $emit-metadata:skip-datum-loop/disp8
1439 $emit-metadata:emit:
1440     # slice->start = ecx
1441     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # copy ecx to *ebx
1442     # write-slice-buffered(out, slice)
1443     # . . push args
1444     53/push-ebx
1445     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1446     # . . call
1447     e8/call  write-slice-buffered/disp32
1448     # . . discard args
1449     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           8/imm32      .                    # add to esp
1450 $emit-metadata:end:
1451     # . reclaim locals
1452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           8/imm32      .                    # add to esp
1453     # . restore registers
1454     5e/pop-to-esi
1455     5b/pop-to-ebx
1456     5a/pop-to-edx
1457     59/pop-to-ecx
1458     58/pop-to-eax
1459     # . epilogue
1460     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1461     5d/pop-to-ebp
1462     c3/return
1463 
1464 test-emit-metadata:
1465     # . prologue
1466     55/push-ebp
1467     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1468     # setup
1469     # . clear-stream(_test-output-stream)
1470     # . . push args
1471     68/push  _test-output-stream/imm32
1472     # . . call
1473     e8/call  clear-stream/disp32
1474     # . . discard args
1475     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1476     # . clear-stream(_test-output-buffered-file->buffer)
1477     # . . push args
1478     68/push  _test-output-buffered-file->buffer/imm32
1479     # . . call
1480     e8/call  clear-stream/disp32
1481     # . . discard args
1482     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1483     # (eax..ecx) = "abc/def"
1484     b8/copy-to-eax  "abc/def"/imm32
1485     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1486     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
1487     05/add-to-eax  4/imm32
1488     # var slice/ecx = {eax, ecx}
1489     51/push-ecx
1490     50/push-eax
1491     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1492     # emit-metadata(_test-output-buffered-file, slice)
1493     # . . push args
1494     51/push-ecx
1495     68/push  _test-output-buffered-file/imm32
1496     # . . call
1497     e8/call  emit-metadata/disp32
1498     # . . discard args
1499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1500     # flush(_test-output-buffered-file)
1501     # . . push args
1502     68/push  _test-output-buffered-file/imm32
1503     # . . call
1504     e8/call  flush/disp32
1505     # . . discard args
1506     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1507     # check-stream-equal(_test-output-stream, "/def", msg)  # important that there's no leading space
1508     # . . push args
1509     68/push  "F - test-emit-metadata"/imm32
1510     68/push  "/def"/imm32
1511     68/push  _test-output-stream/imm32
1512     # . . call
1513     e8/call  check-stream-equal/disp32
1514     # . . discard args
1515     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1516     # . epilogue
1517     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1518     5d/pop-to-ebp
1519     c3/return
1520 
1521 test-emit-metadata-none:
1522     # . prologue
1523     55/push-ebp
1524     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1525     # setup
1526     # . clear-stream(_test-output-stream)
1527     # . . push args
1528     68/push  _test-output-stream/imm32
1529     # . . call
1530     e8/call  clear-stream/disp32
1531     # . . discard args
1532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1533     # . clear-stream(_test-output-buffered-file->buffer)
1534     # . . push args
1535     68/push  _test-output-buffered-file->buffer/imm32
1536     # . . call
1537     e8/call  clear-stream/disp32
1538     # . . discard args
1539     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1540     # (eax..ecx) = "abc"
1541     b8/copy-to-eax  "abc"/imm32
1542     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1543     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
1544     05/add-to-eax  4/imm32
1545     # var slice/ecx = {eax, ecx}
1546     51/push-ecx
1547     50/push-eax
1548     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1549     # emit-metadata(_test-output-buffered-file, slice)
1550     # . . push args
1551     51/push-ecx
1552     68/push  _test-output-buffered-file/imm32
1553     # . . call
1554     e8/call  emit-metadata/disp32
1555     # . . discard args
1556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1557     # flush(_test-output-buffered-file)
1558     # . . push args
1559     68/push  _test-output-buffered-file/imm32
1560     # . . call
1561     e8/call  flush/disp32
1562     # . . discard args
1563     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1564     # check-stream-equal(_test-output-stream, "", msg)
1565     # . . push args
1566     68/push  "F - test-emit-metadata-none"/imm32
1567     68/push  ""/imm32
1568     68/push  _test-output-stream/imm32
1569     # . . call
1570     e8/call  check-stream-equal/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1573     # . epilogue
1574     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1575     5d/pop-to-ebp
1576     c3/return
1577 
1578 test-emit-metadata-multiple:
1579     # . prologue
1580     55/push-ebp
1581     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1582     # setup
1583     # . clear-stream(_test-output-stream)
1584     # . . push args
1585     68/push  _test-output-stream/imm32
1586     # . . call
1587     e8/call  clear-stream/disp32
1588     # . . discard args
1589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1590     # . clear-stream(_test-output-buffered-file->buffer)
1591     # . . push args
1592     68/push  _test-output-buffered-file->buffer/imm32
1593     # . . call
1594     e8/call  clear-stream/disp32
1595     # . . discard args
1596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1597     # (eax..ecx) = "abc/def/ghi"
1598     b8/copy-to-eax  "abc/def/ghi"/imm32
1599     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1600     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
1601     05/add-to-eax  4/imm32
1602     # var slice/ecx = {eax, ecx}
1603     51/push-ecx
1604     50/push-eax
1605     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1606     # emit-metadata(_test-output-buffered-file, slice)
1607     # . . push args
1608     51/push-ecx
1609     68/push  _test-output-buffered-file/imm32
1610     # . . call
1611     e8/call  emit-metadata/disp32
1612     # . . discard args
1613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1614     # flush(_test-output-buffered-file)
1615     # . . push args
1616     68/push  _test-output-buffered-file/imm32
1617     # . . call
1618     e8/call  flush/disp32
1619     # . . discard args
1620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1621     # check-stream-equal(_test-output-stream, "/def/ghi", msg)  # important that there's no leading space
1622     # . . push args
1623     68/push  "F - test-emit-metadata-multiple"/imm32
1624     68/push  "/def/ghi"/imm32
1625     68/push  _test-output-stream/imm32
1626     # . . call
1627     e8/call  check-stream-equal/disp32
1628     # . . discard args
1629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1630     # . epilogue
1631     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1632     5d/pop-to-ebp
1633     c3/return
1634 
1635 test-emit-metadata-when-no-datum:
1636     # . prologue
1637     55/push-ebp
1638     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1639     # setup
1640     # . clear-stream(_test-output-stream)
1641     # . . push args
1642     68/push  _test-output-stream/imm32
1643     # . . call
1644     e8/call  clear-stream/disp32
1645     # . . discard args
1646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1647     # . clear-stream(_test-output-buffered-file->buffer)
1648     # . . push args
1649     68/push  _test-output-buffered-file->buffer/imm32
1650     # . . call
1651     e8/call  clear-stream/disp32
1652     # . . discard args
1653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1654     # var slice/ecx = "/abc"
1655     b8/copy-to-eax  "/abc"/imm32
1656     # . push end/ecx
1657     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1658     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
1659     51/push-ecx
1660     # . push curr/eax
1661     05/add-to-eax  4/imm32
1662     50/push-eax
1663     # . save stack pointer
1664     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1665     # emit-metadata(_test-output-buffered-file, slice)
1666     # . . push args
1667     51/push-ecx
1668     68/push  _test-output-buffered-file/imm32
1669     # . . call
1670     e8/call  emit-metadata/disp32
1671     # . . discard args
1672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1673     # flush(_test-output-buffered-file)
1674     # . . push args
1675     68/push  _test-output-buffered-file/imm32
1676     # . . call
1677     e8/call  flush/disp32
1678     # . . discard args
1679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1680     # check-stream-equal(_test-output-stream, "/abc", msg)  # nothing skipped
1681     # . . push args
1682     68/push  "F - test-emit-metadata-when-no-datum"/imm32
1683     68/push  "/abc"/imm32
1684     68/push  _test-output-stream/imm32
1685     # . . call
1686     e8/call  check-stream-equal/disp32
1687     # . . discard args
1688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1689     # . epilogue
1690     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1691     5d/pop-to-ebp
1692     c3/return
1693 
1694 test-emit-metadata-in-string-literal:
1695     # . prologue
1696     55/push-ebp
1697     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1698     # setup
1699     # . clear-stream(_test-output-stream)
1700     # . . push args
1701     68/push  _test-output-stream/imm32
1702     # . . call
1703     e8/call  clear-stream/disp32
1704     # . . discard args
1705     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1706     # . clear-stream(_test-output-buffered-file->buffer)
1707     # . . push args
1708     68/push  _test-output-buffered-file->buffer/imm32
1709     # . . call
1710     e8/call  clear-stream/disp32
1711     # . . discard args
1712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1713     # var slice/ecx = "\"abc/def\"/ghi"
1714     68/push  _test-slice-literal-string-with-limit/imm32
1715     68/push  _test-slice-literal-string/imm32/start
1716     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1717     # emit-metadata(_test-output-buffered-file, slice)
1718     # . . push args
1719     51/push-ecx
1720     68/push  _test-output-buffered-file/imm32
1721     # . . call
1722     e8/call  emit-metadata/disp32
1723     # . . discard args
1724     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1725     # flush(_test-output-buffered-file)
1726     # . . push args
1727     68/push  _test-output-buffered-file/imm32
1728     # . . call
1729     e8/call  flush/disp32
1730     # . . discard args
1731     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1732 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1758     # check-stream-equal(_test-output-stream, "/ghi", msg)  # important that there's no leading space
1759     # . . push args
1760     68/push  "F - test-emit-metadata-in-string-literal"/imm32
1761     68/push  "/ghi"/imm32
1762     68/push  _test-output-stream/imm32
1763     # . . call
1764     e8/call  check-stream-equal/disp32
1765     # . . discard args
1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1767     # . epilogue
1768     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1769     5d/pop-to-ebp
1770     c3/return
1771 
1772 string-length-at-start-of-slice:  # curr : (address byte), end : (address byte) -> length/eax
1773     # . prologue
1774     55/push-ebp
1775     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1776     # . save registers
1777     51/push-ecx
1778     52/push-edx
1779     53/push-ebx
1780     # ecx = curr
1781     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1782     # edx = end
1783     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1784     # length/eax = 0
1785     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1786     # ebx = 0
1787     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
1788     # skip initial dquote
1789     41/increment-ecx
1790 $string-length-at-start-of-slice:loop:
1791     # if (curr >= end) return length
1792     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1793     73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
1794     # BL = *curr
1795     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
1796 $string-length-at-start-of-slice:dquote:
1797     # if (ebx == '"') break
1798     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x22/imm32/dquote # compare ebx
1799     74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
1800 $string-length-at-start-of-slice:check-for-escape:
1801     # if (ebx == '\') escape next char
1802     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x5c/imm32/backslash # compare ebx
1803     75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
1804 $string-length-at-start-of-slice:escape:
1805     # increment curr but not result
1806     41/increment-ecx
1807 $string-length-at-start-of-slice:continue:
1808     # ++result
1809     40/increment-eax
1810     # ++curr
1811     41/increment-ecx
1812     eb/jump  $string-length-at-start-of-slice:loop/disp8
1813 $string-length-at-start-of-slice:end:
1814     # . restore registers
1815     5b/pop-to-ebx
1816     5a/pop-to-edx
1817     59/pop-to-ecx
1818     # . epilogue
1819     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1820     5d/pop-to-ebp
1821     c3/return
1822 
1823 test-string-length-at-start-of-slice:
1824     # . prologue
1825     55/push-ebp
1826     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1827     # setup: (eax..ecx) = "\"abc\" def"
1828     b8/copy-to-eax  "\"abc\" def"/imm32
1829     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1830     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
1831     05/add-to-eax  4/imm32
1832     # eax = string-length-at-start-of-slice(eax, ecx)
1833     # . . push args
1834     51/push-ecx
1835     50/push-eax
1836     # . . call
1837     e8/call  string-length-at-start-of-slice/disp32
1838     # . . discard args
1839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1840     # check-ints-equal(eax, 3, msg)
1841     # . . push args
1842     68/push  "F - test-string-length-at-start-of-slice"/imm32
1843     68/push  3/imm32
1844     50/push-eax
1845     # . . call
1846     e8/call  check-ints-equal/disp32
1847     # . . discard args
1848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1849     # . epilogue
1850     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1851     5d/pop-to-ebp
1852     c3/return
1853 
1854 test-string-length-at-start-of-slice-escaped:
1855     # . prologue
1856     55/push-ebp
1857     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1858     # setup: (eax..ecx) = "\"ab\\c\" def"
1859     b8/copy-to-eax  "\"ab\\c\" def"/imm32
1860     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1861     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
1862     05/add-to-eax  4/imm32
1863     # eax = string-length-at-start-of-slice(eax, ecx)
1864     # . . push args
1865     51/push-ecx
1866     50/push-eax
1867     # . . call
1868     e8/call  string-length-at-start-of-slice/disp32
1869     # . . discard args
1870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1871     # check-ints-equal(eax, 3, msg)
1872     # . . push args
1873     68/push  "F - test-string-length-at-start-of-slice-escaped"/imm32
1874     68/push  3/imm32
1875     50/push-eax
1876     # . . call
1877     e8/call  check-ints-equal/disp32
1878     # . . discard args
1879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1880     # . epilogue
1881     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1882     5d/pop-to-ebp
1883     c3/return
1884 
1885 == data
1886 
1887 Next-string-literal:  # tracks the next auto-generated variable name
1888   1/imm32
1889 
1890 _test-slice-abc:
1891   22/dquote 61/a 62/b 63/c 22/dquote  # "abc"
1892   2f/slash 64/d
1893 _test-slice-abc-limit:
1894 
1895 _test-slice-a-space-b:
1896   22/dquote 61/a 20/space 62/b 22/dquote  # "a b"
1897 _test-slice-a-space-b-limit:
1898 
1899 _test-slice-a-dquote-b:
1900   22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote  # "a\"b"
1901 _test-slice-a-dquote-b-limit:
1902 
1903 _test-slice-a-newline-b:
1904   22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote  # "a\nb"
1905 _test-slice-a-newline-b-limit:
1906 
1907 # "abc/def"/ghi
1908 _test-slice-literal-string:
1909   22/dquote
1910   61/a 62/b 63/c  # abc
1911   2f/slash 64/d 65/e 66/f  # /def
1912   22/dquote
1913   2f/slash 67/g 68/h 69/i  # /ghi
1914 _test-slice-literal-string-with-limit:
1915 
1916 # . . vim:nowrap:textwidth=0