https://github.com/akkartik/mu/blob/master/subx/apps/assort.subx
   1 # Read a series of segments from stdin and concatenate segments with the same
   2 # name on stdout.
   3 #
   4 # Segments are emitted in order of first encounter.
   5 #
   6 # Drop lines that are all comments. They could get misleading after assortment
   7 # because we don't know if they refer to the line above or the line below.
   8 #
   9 # To run (from the subx/ directory):
  10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
  11 #   $ cat x
  12 #   == code
  13 #   abc
  14 #   == code
  15 #   def
  16 #   $ cat x  |./subx run apps/assort
  17 #   == code
  18 #   abc
  19 #   def
  20 
  21 == code
  22 #   instruction                     effective address                                                   register    displacement    immediate
  23 # . op          subop               mod             rm32          base        index         scale       r32
  24 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  25 
  26 Entry:  # run tests if necessary, convert stdin if not
  27 
  28     # for debugging: run a single test
  29 #?     e8/call test-convert/disp32
  30 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  31 #?     eb/jump  $main:end/disp8
  32 
  33     # . prolog
  34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  35     # - if argc > 1 and argv[1] == "test", then return run_tests()
  36     # . argc > 1
  37     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  38     7e/jump-if-lesser-or-equal  $run-main/disp8
  39     # . argv[1] == "test"
  40     # . . push args
  41     68/push  "test"/imm32
  42     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  43     # . . call
  44     e8/call  kernel-string-equal?/disp32
  45     # . . discard args
  46     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  47     # . check result
  48     3d/compare-EAX-and  1/imm32
  49     75/jump-if-not-equal  $run-main/disp8
  50     # . run-tests()
  51     e8/call  run-tests/disp32
  52     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  53     eb/jump  $main:end/disp8
  54 $run-main:
  55     # - otherwise convert stdin
  56     # var ed/EAX : exit-descriptor
  57     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  58     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  59     # configure ed to really exit()
  60     # . ed->target = 0
  61     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  62     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  63     # . . push args
  64     50/push-EAX/ed
  65     68/push  Stderr/imm32
  66     68/push  Stdout/imm32
  67     68/push  Stdin/imm32
  68     # . . call
  69     e8/call  convert/disp32
  70     # . . discard args
  71     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  72     # . syscall(exit, 0)
  73     bb/copy-to-EBX  0/imm32
  74 $main:end:
  75     b8/copy-to-EAX  1/imm32/exit
  76     cd/syscall  0x80/imm8
  77 
  78 # data structure:
  79 #   row: pair of (address array byte) and (address stream byte)
  80 #   table: (address stream row)
  81 
  82 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  83     # pseudocode:
  84     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
  85     #   read-segments(in, table)
  86     #   write-segments(out, table)
  87     #
  88     # . prolog
  89     55/push-EBP
  90     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  91     # . save registers
  92     51/push-ECX
  93     # var table/ECX : (address stream byte) = stream(10 * 8)
  94     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
  95     68/push  0x50/imm32/length
  96     68/push  0/imm32/read
  97     68/push  0/imm32/write
  98     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
  99     # clear-stream(table)
 100     # . . push args
 101     51/push-ECX
 102     # . . call
 103     e8/call  clear-stream/disp32
 104     # . . discard args
 105     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 106 $convert:read:
 107     # read-segments(in, table)
 108     # . . push args
 109     51/push-ECX
 110     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 111     # . . call
 112     e8/call  read-segments/disp32
 113     # . . discard args
 114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 115 $convert:write:
 116     # write-segments(out, table)
 117     # . . push args
 118     51/push-ECX
 119     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 120     # . . call
 121     e8/call  write-segments/disp32
 122     # . . discard args
 123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 124 $convert:end:
 125     # . reclaim locals
 126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
 127     # . restore registers
 128     59/pop-to-ECX
 129     # . epilog
 130     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 131     5d/pop-to-EBP
 132     c3/return
 133 
 134 #? test-convert:
 135 #?     # . prolog
 136 #?     55/push-EBP
 137 #?     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 138 #?     # setup
 139 #?     # . clear-stream(_test-input-stream)
 140 #?     # . . push args
 141 #?     68/push  _test-input-stream/imm32
 142 #?     # . . call
 143 #?     e8/call  clear-stream/disp32
 144 #?     # . . discard args
 145 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 146 #?     # . clear-stream(_test-input-buffered-file+4)
 147 #?     # . . push args
 148 #?     b8/copy-to-EAX  _test-input-buffered-file/imm32
 149 #?     05/add-to-EAX  4/imm32
 150 #?     50/push-EAX
 151 #?     # . . call
 152 #?     e8/call  clear-stream/disp32
 153 #?     # . . discard args
 154 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 155 #?     # . clear-stream(_test-output-stream)
 156 #?     # . . push args
 157 #?     68/push  _test-output-stream/imm32
 158 #?     # . . call
 159 #?     e8/call  clear-stream/disp32
 160 #?     # . . discard args
 161 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 162 #?     # . clear-stream(_test-output-buffered-file+4)
 163 #?     # . . push args
 164 #?     b8/copy-to-EAX  _test-output-buffered-file/imm32
 165 #?     05/add-to-EAX  4/imm32
 166 #?     50/push-EAX
 167 #?     # . . call
 168 #?     e8/call  clear-stream/disp32
 169 #?     # . . discard args
 170 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 171 #?     # initialize input (meta comments in parens)
 172 #?     #   # comment 1
 173 #?     #     # comment 2 indented
 174 #?     #   == code  (new segment)
 175 #?     #   # comment 3 inside a segment
 176 #?     #   1
 177 #?     #                         (empty line)
 178 #?     #   2 3 # comment 4 inline with other contents
 179 #?     #   == data  (new segment)
 180 #?     #   4 5/imm32
 181 #?     #   == code  (existing segment but non-contiguous with previous iteration)
 182 #?     #   6 7
 183 #?     #   8 9  (multiple lines)
 184 #?     #   == code  (existing segment contiguous with previous iteration)
 185 #?     #   10 11
 186 #?     # . write(_test-input-stream, "# comment 1")
 187 #?     # . . push args
 188 #?     68/push  "# comment 1"/imm32
 189 #?     68/push  _test-input-stream/imm32
 190 #?     # . . call
 191 #?     e8/call  write/disp32
 192 #?     # . . discard args
 193 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 194 #?     # . write(_test-input-stream, "\n")
 195 #?     # . . push args
 196 #?     68/push  Newline/imm32
 197 #?     68/push  _test-input-stream/imm32
 198 #?     # . . call
 199 #?     e8/call  write/disp32
 200 #?     # . . discard args
 201 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 202 #?     # . write(_test-input-stream, "  # comment 2 indented")
 203 #?     # . . push args
 204 #?     68/push  "  # comment 2 indented"/imm32
 205 #?     68/push  _test-input-stream/imm32
 206 #?     # . . call
 207 #?     e8/call  write/disp32
 208 #?     # . . discard args
 209 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 210 #?     # . write(_test-input-stream, "\n")
 211 #?     # . . push args
 212 #?     68/push  Newline/imm32
 213 #?     68/push  _test-input-stream/imm32
 214 #?     # . . call
 215 #?     e8/call  write/disp32
 216 #?     # . . discard args
 217 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 218 #?     # . write(_test-input-stream, "== code")
 219 #?     # . . push args
 220 #?     68/push  "== code"/imm32
 221 #?     68/push  _test-input-stream/imm32
 222 #?     # . . call
 223 #?     e8/call  write/disp32
 224 #?     # . . discard args
 225 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 226 #?     # . write(_test-input-stream, "\n")
 227 #?     # . . push args
 228 #?     68/push  Newline/imm32
 229 #?     68/push  _test-input-stream/imm32
 230 #?     # . . call
 231 #?     e8/call  write/disp32
 232 #?     # . . discard args
 233 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 234 #?     # . write(_test-input-stream, "# comment 3 inside a segment")
 235 #?     # . . push args
 236 #?     68/push  "# comment 3 inside a segment"/imm32
 237 #?     68/push  _test-input-stream/imm32
 238 #?     # . . call
 239 #?     e8/call  write/disp32
 240 #?     # . . discard args
 241 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 242 #?     # . write(_test-input-stream, "\n")
 243 #?     # . . push args
 244 #?     68/push  Newline/imm32
 245 #?     68/push  _test-input-stream/imm32
 246 #?     # . . call
 247 #?     e8/call  write/disp32
 248 #?     # . . discard args
 249 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 250 #?     # . write(_test-input-stream, "1")
 251 #?     # . . push args
 252 #?     68/push  "1"/imm32
 253 #?     68/push  _test-input-stream/imm32
 254 #?     # . . call
 255 #?     e8/call  write/disp32
 256 #?     # . . discard args
 257 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 258 #?     # . write(_test-input-stream, "\n")
 259 #?     # . . push args
 260 #?     68/push  Newline/imm32
 261 #?     68/push  _test-input-stream/imm32
 262 #?     # . . call
 263 #?     e8/call  write/disp32
 264 #?     # . . discard args
 265 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 266 #?     # . write(_test-input-stream, "\n")  # empty line
 267 #?     # . . push args
 268 #?     68/push  Newline/imm32
 269 #?     68/push  _test-input-stream/imm32
 270 #?     # . . call
 271 #?     e8/call  write/disp32
 272 #?     # . . discard args
 273 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 274 #?     # . write(_test-input-stream, "2 3 # comment 4 inline with other comments")
 275 #?     # . . push args
 276 #?     68/push  "2 3 # comment 4 inline with other comments"/imm32
 277 #?     68/push  _test-input-stream/imm32
 278 #?     # . . call
 279 #?     e8/call  write/disp32
 280 #?     # . . discard args
 281 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 282 #?     # . write(_test-input-stream, "\n")
 283 #?     # . . push args
 284 #?     68/push  Newline/imm32
 285 #?     68/push  _test-input-stream/imm32
 286 #?     # . . call
 287 #?     e8/call  write/disp32
 288 #?     # . . discard args
 289 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 290 #?     # . write(_test-input-stream, "== data")
 291 #?     # . . push args
 292 #?     68/push  "== data"/imm32
 293 #?     68/push  _test-input-stream/imm32
 294 #?     # . . call
 295 #?     e8/call  write/disp32
 296 #?     # . . discard args
 297 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 298 #?     # . write(_test-input-stream, "\n")
 299 #?     # . . push args
 300 #?     68/push  Newline/imm32
 301 #?     68/push  _test-input-stream/imm32
 302 #?     # . . call
 303 #?     e8/call  write/disp32
 304 #?     # . . discard args
 305 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 306 #?     # . write(_test-input-stream, "4 5/imm32")
 307 #?     # . . push args
 308 #?     68/push  "4 5/imm32"/imm32
 309 #?     68/push  _test-input-stream/imm32
 310 #?     # . . call
 311 #?     e8/call  write/disp32
 312 #?     # . . discard args
 313 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 314 #?     # . write(_test-input-stream, "\n")
 315 #?     # . . push args
 316 #?     68/push  Newline/imm32
 317 #?     68/push  _test-input-stream/imm32
 318 #?     # . . call
 319 #?     e8/call  write/disp32
 320 #?     # . . discard args
 321 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 322 #?     # . write(_test-input-stream, "== code")
 323 #?     # . . push args
 324 #?     68/push  "== code"/imm32
 325 #?     68/push  _test-input-stream/imm32
 326 #?     # . . call
 327 #?     e8/call  write/disp32
 328 #?     # . . discard args
 329 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 330 #?     # . write(_test-input-stream, "\n")
 331 #?     # . . push args
 332 #?     68/push  Newline/imm32
 333 #?     68/push  _test-input-stream/imm32
 334 #?     # . . call
 335 #?     e8/call  write/disp32
 336 #?     # . . discard args
 337 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 338 #?     # . write(_test-input-stream, "6 7")
 339 #?     # . . push args
 340 #?     68/push  "6 7"/imm32
 341 #?     68/push  _test-input-stream/imm32
 342 #?     # . . call
 343 #?     e8/call  write/disp32
 344 #?     # . . discard args
 345 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 346 #?     # . write(_test-input-stream, "\n")
 347 #?     # . . push args
 348 #?     68/push  Newline/imm32
 349 #?     68/push  _test-input-stream/imm32
 350 #?     # . . call
 351 #?     e8/call  write/disp32
 352 #?     # . . discard args
 353 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 354 #?     # . write(_test-input-stream, "8 9")
 355 #?     # . . push args
 356 #?     68/push  "6 7"/imm32
 357 #?     68/push  _test-input-stream/imm32
 358 #?     # . . call
 359 #?     e8/call  write/disp32
 360 #?     # . . discard args
 361 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 362 #?     # . write(_test-input-stream, "\n")
 363 #?     # . . push args
 364 #?     68/push  Newline/imm32
 365 #?     68/push  _test-input-stream/imm32
 366 #?     # . . call
 367 #?     e8/call  write/disp32
 368 #?     # . . discard args
 369 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 370 #?     # . write(_test-input-stream, "== code")
 371 #?     # . . push args
 372 #?     68/push  "== code"/imm32
 373 #?     68/push  _test-input-stream/imm32
 374 #?     # . . call
 375 #?     e8/call  write/disp32
 376 #?     # . . discard args
 377 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 378 #?     # . write(_test-input-stream, "\n")
 379 #?     # . . push args
 380 #?     68/push  Newline/imm32
 381 #?     68/push  _test-input-stream/imm32
 382 #?     # . . call
 383 #?     e8/call  write/disp32
 384 #?     # . . discard args
 385 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 386 #?     # . write(_test-input-stream, "10 11")
 387 #?     # . . push args
 388 #?     68/push  "10 11"/imm32
 389 #?     68/push  _test-input-stream/imm32
 390 #?     # . . call
 391 #?     e8/call  write/disp32
 392 #?     # . . discard args
 393 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 394 #?     # . write(_test-input-stream, "\n")
 395 #?     # . . push args
 396 #?     68/push  Newline/imm32
 397 #?     68/push  _test-input-stream/imm32
 398 #?     # . . call
 399 #?     e8/call  write/disp32
 400 #?     # . . discard args
 401 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 402 #?     # convert(_test-input-buffered-file, _test-output-buffered-file)
 403 #?     # . . push args
 404 #?     68/push  _test-output-buffered-file/imm32
 405 #?     68/push  _test-input-buffered-file/imm32
 406 #?     # . . call
 407 #?     e8/call  convert/disp32
 408 #?     # . . discard args
 409 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 410 #?     # check output
 411 #?     #   == code
 412 #?     #   1
 413 #?     #   2 3 # comment 4 inline with other contents
 414 #?     #   6 7
 415 #?     #   8 9
 416 #?     #   10 11
 417 #?     #   == data
 418 #?     #   4 5/imm32
 419 +-- 34 lines: #? #?     # debug print ------------------------------------------------------------------------------------------------------------------------
 453 #?     # . flush(_test-output-buffered-file)
 454 #?     # . . push args
 455 #?     68/push  _test-output-buffered-file/imm32
 456 #?     # . . call
 457 #?     e8/call  flush/disp32
 458 #?     # . . discard args
 459 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 460 #?     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
 461 #?     # . . push args
 462 #?     68/push  "F - test-convert-code-and-data-segments/0"/imm32
 463 #?     68/push  "== code"/imm32
 464 #?     68/push  _test-output-stream/imm32
 465 #?     # . . call
 466 #?     e8/call  check-next-stream-line-equal/disp32
 467 #?     # . . discard args
 468 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 469 #?     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
 470 #?     # . . push args
 471 #?     68/push  "F - test-convert-code-and-data-segments/1"/imm32
 472 #?     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 473 #?     68/push  _test-output-stream/imm32
 474 #?     # . . call
 475 #?     e8/call  check-next-stream-line-equal/disp32
 476 #?     # . . discard args
 477 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 478 #?     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
 479 #?     # . . push args
 480 #?     68/push  "F - test-convert-code-and-data-segments/2"/imm32
 481 #?     68/push  "2 3 # comment 4 inline with other contents"/imm32
 482 #?     68/push  _test-output-stream/imm32
 483 #?     # . . call
 484 #?     e8/call  check-next-stream-line-equal/disp32
 485 #?     # . . discard args
 486 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 487 #?     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
 488 #?     # . . push args
 489 #?     68/push  "F - test-convert-code-and-data-segments/3"/imm32
 490 #?     68/push  "6 7"/imm32
 491 #?     68/push  _test-output-stream/imm32
 492 #?     # . . call
 493 #?     e8/call  check-next-stream-line-equal/disp32
 494 #?     # . . discard args
 495 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 496 #?     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
 497 #?     # . . push args
 498 #?     68/push  "F - test-convert-code-and-data-segments/4"/imm32
 499 #?     68/push  "8 9"/imm32
 500 #?     68/push  _test-output-stream/imm32
 501 #?     # . . call
 502 #?     e8/call  check-next-stream-line-equal/disp32
 503 #?     # . . discard args
 504 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 505 #?     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
 506 #?     # . . push args
 507 #?     68/push  "F - test-convert-code-and-data-segments/5"/imm32
 508 #?     68/push  "10 11"/imm32
 509 #?     68/push  _test-output-stream/imm32
 510 #?     # . . call
 511 #?     e8/call  check-next-stream-line-equal/disp32
 512 #?     # . . discard args
 513 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 514 #?     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
 515 #?     # . . push args
 516 #?     68/push  "F - test-convert-code-and-data-segments/6"/imm32
 517 #?     68/push  "== data"/imm32
 518 #?     68/push  _test-output-stream/imm32
 519 #?     # . . call
 520 #?     e8/call  check-next-stream-line-equal/disp32
 521 #?     # . . discard args
 522 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 523 #?     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
 524 #?     # . . push args
 525 #?     68/push  "F - test-convert-code-and-data-segments/4"/imm32
 526 #?     68/push  "4 5/imm32"/imm32
 527 #?     68/push  _test-output-stream/imm32
 528 #?     # . . call
 529 #?     e8/call  check-next-stream-line-equal/disp32
 530 #?     # . . discard args
 531 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 532 #?     # . epilog
 533 #?     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 534 #?     5d/pop-to-EBP
 535 #?     c3/return
 536 
 537 read-segments:  # in : (address buffered-file), table : (address stream row)
 538     # pseudocode:
 539     #   var curr-segment = null
 540     #   var line = new-stream(512, 1)
 541     #   while true
 542     #     clear-stream(line)
 543     #     read-line(in, line)
 544     #     if (line->write == 0) break             # end of file
 545     #     var word-slice = next-word(line)
 546     #     if slice-empty?(word-slice)             # whitespace
 547     #       continue
 548     #     if slice-starts-with?(word-slice, "#")  # comment
 549     #       continue
 550     #     if (slice-equal?(word-slice, "=="))
 551     #       var segment-name = next-word(line)
 552     #       curr-segment = get-or-insert-segment(table, segment-name, N)
 553     #     else
 554     #       write-stream-data(curr-segment, line)
 555     #
 556     # . prolog
 557     55/push-EBP
 558     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 559     # . save registers
 560     51/push-ECX
 561     # var line/ECX : (address stream byte) = stream(512)
 562     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 563     68/push  0x200/imm32/length
 564     68/push  0/imm32/read
 565     68/push  0/imm32/write
 566     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 567     # var word-slice/EDX = {0, 0}
 568     68/push  0/imm32/end
 569     68/push  0/imm32/curr
 570     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 571     # var curr-segment/EBX = null
 572     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 573 $read-segments:loop:
 574     # clear-stream(line)
 575     # . . push args
 576     51/push-ECX
 577     # . . call
 578     e8/call  clear-stream/disp32
 579     # . . discard args
 580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 581     # read-line(in, line)
 582     # . . push args
 583     51/push-ECX
 584     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 585     # . . call
 586     e8/call  read-line/disp32
 587     # . . discard args
 588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 589 $read-segments:check0:
 590     # if (line->write == 0) break
 591     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 592     0f 84/jump-if-equal  $read-segments:break/disp32
 593 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 627     # next-word(line, word-slice)
 628     # . . push args
 629     52/push-EDX
 630     51/push-ECX
 631     # . . call
 632     e8/call  next-word/disp32
 633     # . . discard args
 634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 635 $read-segments:check1:
 636     # if (slice-empty?(word-slice)) continue
 637     # . EAX = slice-empty?(word-slice)
 638     # . . push args
 639     52/push-EDX
 640     # . . call
 641     e8/call  slice-empty?/disp32
 642     # . . discard args
 643     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 644     # . if (EAX != 0) continue
 645     3d/compare-EAX-and  0/imm32
 646     0f 85/jump-if-not-equal  $read-segments:loop/disp32
 647 $read-segments:check-for-comment:
 648     # if (slice-starts-with?(word-slice, "#"))
 649     # . start/EDX = word-slice->start
 650     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
 651     # . c/EAX = *start
 652     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 653     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
 654     # . if (EAX == '#') continue
 655     3d/compare-EAX-and  0x23/imm32/hash
 656     74/jump-if-equal  $read-segments:loop/disp8
 657 $read-segments:check-for-segment-header:
 658 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 708     # if (slice-equal?(word-slice, "=="))
 709     #   segment-name = next-word(line)
 710     #   curr-segment = get-or-insert(table, segment-name)
 711     # . EAX = slice-equal?(word-slice, "==")
 712     # . . push args
 713     68/push  "=="/imm32
 714     52/push-EDX
 715     # . . call
 716     e8/call  slice-equal?/disp32
 717     # . . discard args
 718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 719     # . if (EAX == 0) goto check3
 720     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
 721     0f 84/jump-if-equal  $read-segments:regular-line/disp32
 722     # . next-word(line, segment-name)
 723     # . . push args
 724     52/push-EDX
 725     51/push-ECX
 726     # . . call
 727     e8/call  next-word/disp32
 728     # . . discard args
 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 730 +-- 50 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
 780     # . EAX = get-or-insert-segment(table, segment-name, N)
 781     # . . push args
 782     68/push  0x1000/imm32/segment-size/4KB
 783     52/push-EDX
 784     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 785     # . . call
 786     e8/call  get-or-insert-segment/disp32
 787     # . . discard args
 788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 789     # . curr-segment = EAX
 790     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 791     # . continue
 792     e9/jump  $read-segments:loop/disp32
 793 $read-segments:regular-line:
 794     # write-stream-data(curr-segment, line)
 795     # . . push args
 796     51/push-ECX
 797     53/push-EBX
 798     # . . call
 799     e8/call  write-stream-data/disp32
 800     # . . discard args
 801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 802     # loop
 803     e9/jump  $read-segments:loop/disp32
 804 $read-segments:break:
 805 $read-segments:end:
 806     # . reclaim locals
 807     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 808     # . restore registers
 809     59/pop-to-ECX
 810     # . epilog
 811     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 812     5d/pop-to-EBP
 813     c3/return
 814 
 815 write-segments:  # out : (address buffered-file), table : (address stream row)
 816     # pseudocode:
 817     #   name, stream = table[0]
 818     #   var i = 0
 819     #   while i < table.length
 820     #     name = table[i].name
 821     #     if (name == null) break
 822     #     write-buffered(out, "== ")
 823     #     write-buffered(out, name)
 824     #     write-buffered(out, "\n")
 825     #     stream = table[i].stream
 826     #     write-stream-data(out, stream)
 827     #     ++i
 828     #   flush(out)
 829     #
 830     # . prolog
 831     55/push-EBP
 832     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 833     # . save registers
 834 $write-segments:end:
 835     # . reclaim locals
 836     # . restore registers
 837     # . epilog
 838     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 839     5d/pop-to-EBP
 840     c3/return
 841 
 842 ## helpers
 843 
 844 # TODO: pass in an allocation descriptor
 845 get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
 846     # pseudocode:
 847     #   curr = table->data
 848     #   max = &table->data[table->write]
 849     #   while curr < max
 850     #     if slice-equal?(s, *curr)
 851     #       return *(curr+4)
 852     #     curr += 8
 853     #   if table->write < table->length
 854     #     *max = slice-to-string(Heap, s)
 855     #     result = new-stream(Heap, n, 1)
 856     #     *(max+4) = result
 857     #     table->write += 8
 858     #     return result
 859     #   return 0
 860     #
 861     # . prolog
 862     55/push-EBP
 863     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 864     # . save registers
 865     51/push-ECX
 866     52/push-EDX
 867     56/push-ESI
 868     # ESI = table
 869     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 870     # curr/ECX = table->data
 871     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
 872     # max/EDX = table->data + table->write
 873     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 874     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
 875 $get-or-insert-segment:search-loop:
 876     # if (curr >= max) break
 877     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
 878     7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
 879     # if (slice-equal?(s, *curr)) return *(curr+4)
 880     # . EAX = slice-equal?(s, *curr)
 881     # . . push args
 882     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
 883     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 884     # . . call
 885     e8/call  slice-equal?/disp32
 886     # . . discard args
 887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 888     # . if (EAX != 0) return EAX = *(curr+4)
 889     3d/compare-EAX-and  0/imm32
 890     74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
 891     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 892     eb/jump  $get-or-insert-segment:end/disp8
 893 $get-or-insert-segment:mismatch:
 894     # curr += 8
 895     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
 896     # loop
 897     eb/jump  $get-or-insert-segment:search-loop/disp8
 898 $get-or-insert-segment:not-found:
 899     # result/EAX = 0
 900     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 901     # if (table->write >= table->length) abort
 902     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
 903     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
 904     7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
 905     # *max = slice-to-string(Heap, s)
 906     # . EAX = slice-to-string(Heap, s)
 907     # . . push args
 908     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 909     68/push  Heap/imm32
 910     # . . call
 911     e8/call  slice-to-string/disp32
 912     # . . discard args
 913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 914     # . *max = EAX <= writes to 0x0a003873
 915     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
 916     # result/EAX = new-stream(Heap, n, 1)
 917     # . . push args
 918     68/push  1/imm32
 919     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 920     68/push  Heap/imm32
 921     # . . call
 922     e8/call  new-stream/disp32
 923     # . . discard args
 924     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 925     # *(max+4) = result
 926     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
 927     # table->write += 8
 928     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
 929 $get-or-insert-segment:end:
 930     # . restore registers
 931     5e/pop-to-ESI
 932     5a/pop-to-EDX
 933     59/pop-to-ECX
 934     # . epilog
 935     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 936     5d/pop-to-EBP
 937     c3/return
 938 
 939 $get-or-insert-segment:abort:
 940     # . _write(2/stderr, error)
 941     # . . push args
 942     68/push  "get-or-insert-segment: too many segments"/imm32
 943     68/push  2/imm32/stderr
 944     # . . call
 945     e8/call  _write/disp32
 946     # . . discard args
 947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 948     # . syscall(exit, 1)
 949     bb/copy-to-EBX  1/imm32
 950     b8/copy-to-EAX  1/imm32/exit
 951     cd/syscall  0x80/imm8
 952     # never gets here
 953 
 954 test-get-or-insert-segment:
 955     # . prolog
 956     55/push-EBP
 957     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 958     # var table/ECX : (address stream byte) = stream(2 * 8)
 959     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
 960     68/push  0x10/imm32/length
 961     68/push  0/imm32/read
 962     68/push  0/imm32/write
 963     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 964     # EDX : (address slice) = "code"
 965     68/push  _test-code-segment-end/imm32/end
 966     68/push  _test-code-segment/imm32/start
 967     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 968 $test-get-or-insert-segment:first-call:
 969     # - start with an empty table, insert one segment, verify that it was inserted
 970     # segment/EAX = get-or-insert-segment(table, "code" slice, 10)
 971     # . . push args
 972     68/push  0xa/imm32/segment-length
 973     52/push-EDX
 974     51/push-ECX
 975     # . . call
 976     e8/call  get-or-insert-segment/disp32
 977     # . . discard args
 978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 979     # save segment
 980     50/push-EAX
 981     # if (segment != 0) goto next check
 982     3d/compare-EAX-and  0/imm32
 983     75/jump-if-not-equal  $test-get-or-insert-segment:check1/disp8
 984     # fail test
 985     # . _write(2/stderr, msg)
 986     # . . push args
 987     68/push  "F - test-get-or-insert-segment/0"/imm32
 988     68/push  2/imm32/stderr
 989     # . . call
 990     e8/call  _write/disp32
 991     # . . discard args
 992     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 993     # . _write(2/stderr, Newline)
 994     # . . push args
 995     68/push  Newline/imm32
 996     68/push  2/imm32/stderr
 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     # . increment Num-test-failures
1002     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
1003     e9/jump  $test-get-or-insert-segment:end/disp32
1004 $test-get-or-insert-segment:check1:
1005     # check-ints-equal(segment->length, 10, msg)
1006     # . . push args
1007     68/push  "F - test-get-or-insert-segment/1"/imm32
1008     68/push  0xa/imm32/segment-length
1009     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
1010     # . . call
1011     e8/call  check-ints-equal/disp32
1012     # . . discard args
1013     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1014 $test-get-or-insert-segment:check2:
1015     # check-ints-equal(table->write, rowsize = 8, msg)
1016     # . . push args
1017     68/push  "F - test-get-or-insert-segment/2"/imm32
1018     68/push  8/imm32/row-size
1019     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
1020     # . . call
1021     e8/call  check-ints-equal/disp32
1022     # . . discard args
1023     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1024     # EAX = string-equal?(*table->data, "code")
1025     # . . push args
1026     68/push  "code"/imm32
1027     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
1028     # . . call
1029     e8/call  string-equal?/disp32
1030     # . . discard args
1031     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1032     # check-ints-equal(EAX, 1, msg)
1033     # . . push args
1034     68/push  "F - test-get-or-insert-segment/3"/imm32
1035     68/push  1/imm32
1036     50/push-EAX
1037     # . . call
1038     e8/call  check-ints-equal/disp32
1039     # . . discard args
1040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1041 $test-get-or-insert-segment:check3:
1042     # stream/EAX = *(table->data+4)
1043     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
1044     # check-ints-equal(stream->length, 10, msg)
1045     # . . push args
1046     68/push  "F - test-get-or-insert-segment/4"/imm32
1047     68/push  0xa/imm32/segment-size
1048     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
1049     # . . call
1050     e8/call  check-ints-equal/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1053 $test-get-or-insert-segment:second-call:
1054     # - insert the same segment name again, verify that it was reused
1055     # segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
1056     # . . push args
1057     68/push  8/imm32/segment-length
1058     52/push-EDX
1059     51/push-ECX
1060     # . . call
1061     e8/call  get-or-insert-segment/disp32
1062     # . . discard args
1063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1064     # restore old segment1
1065     5a/pop-to-EDX
1066     # check-ints-equal(segment2/EAX, segment1/EDX, msg)
1067     # . . push args
1068     68/push  "F - test-get-or-insert-segment/5"/imm32
1069     52/push-EDX
1070     50/push-EAX
1071     # . . call
1072     e8/call  check-ints-equal/disp32
1073     # . . discard args
1074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1075     # no change to table size
1076     # . check-ints-equal(table->write, rowsize = 8, msg)
1077     # . . push args
1078     68/push  "F - test-get-or-insert-segment/6"/imm32
1079     68/push  8/imm32/row-size
1080     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
1081     # . . call
1082     e8/call  check-ints-equal/disp32
1083     # . . discard args
1084     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1085 $test-get-or-insert-segment:third-call:
1086     # - insert a new segment name, verify that it was inserted
1087     # EDX : (address slice) = "data"
1088     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
1089     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
1090     # segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
1091     # . . push args
1092     68/push  8/imm32/segment-length
1093     52/push-EDX
1094     51/push-ECX
1095     # . . call
1096     e8/call  get-or-insert-segment/disp32
1097     # . . discard args
1098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1099     # table gets a new row
1100     # . check-ints-equal(table->write, 2 rows = 16, msg)
1101     # . . push args
1102     68/push  "F - test-get-or-insert-segment/7"/imm32
1103     68/push  0x10/imm32/two-rows
1104     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
1105     # . . call
1106     e8/call  check-ints-equal/disp32
1107     # . . discard args
1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1109 $test-get-or-insert-segment:end:
1110     # . epilog
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 # write an entire stream's contents to a buffered-file
1116 # ways to do this:
1117 #   - construct a 'maximal slice' and pass it to write-slice
1118 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
1119 # we'll go with the first way for now
1120 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
1121     # . prolog
1122     55/push-EBP
1123     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1124     # . save registers
1125     50/push-EAX
1126     51/push-ECX
1127     56/push-ESI
1128     # ESI = s
1129     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
1130     # var slice/ECX = {s->data, s->data + s->write}
1131     # . push s->data + s->write
1132     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1133     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
1134     50/push-EAX
1135     # . push s->data
1136     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
1137     50/push-EAX
1138     # . ECX = ESP
1139     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1140     # write-slice(f, slice)
1141     # . . push args
1142     51/push-ECX
1143     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1144     # . . call
1145     e8/call  write-slice/disp32
1146     # . . discard args
1147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1148 $write-stream-data:end:
1149     # . restore locals
1150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1151     # . restore registers
1152     5e/pop-to-ESI
1153     59/pop-to-ECX
1154     58/pop-to-EAX
1155     # . epilog
1156     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1157     5d/pop-to-EBP
1158     c3/return
1159 
1160 test-write-stream-data:
1161     # . prolog
1162     55/push-EBP
1163     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1164     # setup
1165     # . clear-stream(_test-stream)
1166     # . . push args
1167     68/push  _test-stream/imm32
1168     # . . call
1169     e8/call  clear-stream/disp32
1170     # . . discard args
1171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1172     # . clear-stream(_test-buffered-file+4)
1173     # . . push args
1174     b8/copy-to-EAX  _test-buffered-file/imm32
1175     05/add-to-EAX  4/imm32
1176     50/push-EAX
1177     # . . call
1178     e8/call  clear-stream/disp32
1179     # . . discard args
1180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1181     # . clear-stream(_test-tmp-stream)
1182     # . . push args
1183     68/push  _test-tmp-stream/imm32
1184     # . . call
1185     e8/call  clear-stream/disp32
1186     # . . discard args
1187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1188     # initialize input
1189     # . write(_test-tmp-stream, "abcd")
1190     # . . push args
1191     68/push  "abcd"/imm32
1192     68/push  _test-tmp-stream/imm32
1193     # . . call
1194     e8/call  write/disp32
1195     # . . discard args
1196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1197     # write-stream-data(_test-buffered-file, _test-tmp-stream)
1198     # . . push args
1199     68/push  _test-tmp-stream/imm32
1200     68/push  _test-buffered-file/imm32
1201     # . . call
1202     e8/call  write-stream-data/disp32
1203     # . . discard args
1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1205     # check that the write happened as expected
1206     # . flush(_test-buffered-file)
1207     # . . push args
1208     68/push  _test-buffered-file/imm32
1209     # . . call
1210     e8/call  flush/disp32
1211     # . . discard args
1212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1213     # . check-stream-equal(_test-stream, "abcd", msg)
1214     # . . push args
1215     68/push  "F - test-write-stream-data"/imm32
1216     68/push  "abcd"/imm32
1217     68/push  _test-stream/imm32
1218     # . . call
1219     e8/call  check-stream-equal/disp32
1220     # . . discard args
1221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1222     # . epilog
1223     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1224     5d/pop-to-EBP
1225     c3/return
1226 
1227 == data
1228 
1229 _test-input-stream:
1230     # current write index
1231     0/imm32
1232     # current read index
1233     0/imm32
1234     # length
1235     0x100/imm32  # 256 bytes
1236     # data (16 lines x 16 bytes/line)
1237     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1238     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1239     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1240     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1241     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1242     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1243     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1244     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1245     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1246     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1247     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1248     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1249     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1250     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1251     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1252     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1253 
1254 # a test buffered file for _test-input-stream
1255 _test-input-buffered-file:
1256     # file descriptor or (address stream)
1257     _test-input-stream/imm32
1258     # current write index
1259     0/imm32
1260     # current read index
1261     0/imm32
1262     # length
1263     6/imm32
1264     # data
1265     00 00 00 00 00 00  # 6 bytes
1266 
1267 _test-output-stream:
1268     # current write index
1269     0/imm32
1270     # current read index
1271     0/imm32
1272     # length
1273     0x100/imm32  # 256 bytes
1274     # data (16 lines x 16 bytes/line)
1275     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1276     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1277     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1278     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1279     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1280     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1281     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1282     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1283     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1284     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1285     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1286     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1287     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1288     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1289     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1290     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1291 
1292 # a test buffered file for _test-output-stream
1293 _test-output-buffered-file:
1294     # file descriptor or (address stream)
1295     _test-output-stream/imm32
1296     # current write index
1297     0/imm32
1298     # current read index
1299     0/imm32
1300     # length
1301     6/imm32
1302     # data
1303     00 00 00 00 00 00  # 6 bytes
1304 
1305 _test-code-segment:
1306   63/c 6f/o 64/d 65/e
1307 _test-code-segment-end:
1308 
1309 _test-data-segment:
1310   64/d 61/a 74/t 61/a
1311 _test-data-segment-end:
1312 
1313 # . . vim:nowrap:textwidth=0