https://github.com/akkartik/mu/blob/master/apps/crenshaw2-1b.subx
  1 # Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas
  2 # which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt
  3 # except that we support hex numbers of multiple digits.
  4 #
  5 # To run:
  6 #   $ ./subx translate init.linux 0*.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b
  7 #   $ echo '1a'  |./subx run apps/crenshaw2-1b
  8 # Expected output:
  9 #   # syscall(exit, 1a)
 10 #   bb/copy-to-ebx  3/imm32
 11 #   b8/copy-to-eax  1/imm32/exit
 12 #   cd/syscall  0x80/imm8
 13 #
 14 # To run the generated output:
 15 #   $ echo '1a'  |./subx run apps/crenshaw2-1b > z1.subx
 16 #   $ ./subx translate init.linux z1.subx -o z1
 17 #   $ ./subx run z1
 18 #   $ echo $?
 19 #   26  # 0x1a in decimal
 20 #
 21 # Stdin must contain just a single hex digit. Other input will print an error:
 22 #   $ echo 'xyz'  |./subx run apps/crenshaw2-1b
 23 #   Error: integer expected
 24 #
 25 # Names in this file sometimes follow Crenshaw's original rather than my usual
 26 # naming conventions.
 27 
 28 == code
 29 #   instruction                     effective address                                                   register    displacement    immediate
 30 # . op          subop               mod             rm32          base        index         scale       r32
 31 # . 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
 32 
 33 Entry:  # run tests if necessary, call 'compile' if not
 34     # . prologue
 35     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 36 
 37     # initialize heap
 38     # . Heap = new-segment(Heap-size)
 39     # . . push args
 40     68/push  Heap/imm32
 41     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
 42     # . . call
 43     e8/call  new-segment/disp32
 44     # . . discard args
 45     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 46 
 47     # - if argc > 1 and argv[1] == "test", then return run_tests()
 48     # if (argc <= 1) goto run-main
 49     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
 50     7e/jump-if-<=  $run-main/disp8
 51     # if (!kernel-string-equal?(argv[1], "test")) goto run-main
 52     # . eax = kernel-string-equal?(argv[1], "test")
 53     # . . push args
 54     68/push  "test"/imm32
 55     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 56     # . . call
 57     e8/call  kernel-string-equal?/disp32
 58     # . . discard args
 59     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 60     # . if (eax == false) goto run-main
 61     3d/compare-eax-and  0/imm32/false
 62     74/jump-if-=  $run-main/disp8
 63     # run-tests()
 64     e8/call  run-tests/disp32
 65     # syscall(exit, *Num-test-failures)
 66     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
 67     eb/jump  $main:end/disp8
 68 $run-main:
 69     # - otherwise read a program from stdin and emit its translation to stdout
 70     # var ed/eax : exit-descriptor
 71     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 72     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
 73     # configure ed to really exit()
 74     # . ed->target = 0
 75     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
 76     # compile(Stdin, 1/stdout, 2/stderr, ed)
 77     # . . push args
 78     50/push-eax/ed
 79     68/push  2/imm32/stderr
 80     68/push  1/imm32/stdout
 81     68/push  Stdin/imm32
 82     # . . call
 83     e8/call  compile/disp32
 84     # . . discard args
 85     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 86     # syscall(exit, 0)
 87     bb/copy-to-ebx  0/imm32
 88 $main:end:
 89     b8/copy-to-eax  1/imm32/exit
 90     cd/syscall  0x80/imm8
 91 
 92 # the main entry point
 93 compile:  # in : (addr buffered-file), out : fd or (addr stream byte), err : fd or (addr stream byte), ed : (addr exit-descriptor)
 94     # . prologue
 95     55/push-ebp
 96     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 97     # . save registers
 98     50/push-eax
 99     51/push-ecx
100     # prime the pump
101     # . Look = get-char(in)
102     # . . push args
103     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8      .                    # push *(ebp+8)
104     # . . call
105     e8/call  get-char/disp32
106     # . . discard args
107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
108     # var num/ecx : (stream byte 7)
109     # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes.
110     # Sizing the stream just right buys us overflow-handling for free inside 'get-num'.
111     # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex.
112     # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point.
113     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x13/imm32        # subtract from esp
114     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
115     # initialize the stream
116     # . num->length = 7
117     c7          0/subop/copy        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           8/disp8         7/imm32           # copy to *(ecx+8)
118     # . clear-stream(num)
119     # . . push args
120     51/push-ecx
121     # . . call
122     e8/call  clear-stream/disp32
123     # . . discard args
124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .          (lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<a href='https://github.com/akkartik/mu/blob/master/apps/ex4.subx'>https://github.com/akkartik/mu/blob/master/apps/ex4.subx</a>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr"> 1 </span><span class="subxComment"># Read a character from stdin, save it to a global, write it to stdout.</span>
<span id="L2" class="LineNr"> 2 </span><span class="subxComment">#</span>
<span id="L3" class="LineNr"> 3 </span><span class="subxComment"># To run:</span>
<span id="L4" class="LineNr"> 4 </span><span class="subxComment">#   $ ./bootstrap translate init.linux apps/ex4.subx -o apps/ex4</span>
<span id="L5" class="LineNr"> 5 </span><span class="subxComment">#   $ ./bootstrap run apps/ex4</span>
<span id="L6" class="LineNr"> 6 </span>
<span id="L7" class="LineNr"> 7 </span>== data
<span id="L8" class="LineNr"> 8 </span>
<span id="L9" class="LineNr"> 9 </span><span class="subxComment"># the global variable we save to</span>
<span id="L10" class="LineNr">10 </span><span class="SpecialChar">X</span>:
<span id="L11" class="LineNr">11 </span>    0/imm32  <span class="subxComment"># space for read() to write to</span>
<span id="L12" class="LineNr">12 </span>
<span id="L13" class="LineNr">13 </span>== code
<span id="L14" class="LineNr">14 </span>
<span id="L15" class="LineNr">15 </span><span class="SpecialChar">Entry</span>:
<span id="L16" class="LineNr">16 </span><span class="subxComment"># read(stdin, X, 1)</span>
<span id="L17" class="LineNr">17 </span><span class="subxS1Comment"># . fd = 0 (stdin)</span>
<span id="L18" class="LineNr">18 </span>bb/copy-to-ebx  0/imm32
<span id="L19" class="LineNr">19 </span><span class="subxS1Comment"># . data = X (location to write result to)</span>
<span id="L20" class="LineNr">20 </span>b9/copy-to-ecx  <span class="SpecialChar"><a href='ex4.subx.html#L10'>X</a></span>/imm32
<span id="L21" class="LineNr">21 </span><span class="subxS1Comment"># . size = 1 character</span>
<span id="L22" class="LineNr">22 </span>ba/copy-to-edx  1/imm32
<span id="L23" class="LineNr">23 </span><span class="subxS1Comment"># . syscall</span>
<span id="L24" class="LineNr">24 </span>e8/call  syscall_read/disp32
<span id="L25" class="LineNr">25 </span>
<span id="L26" class="LineNr">26 </span><span class="subxComment"># write(stdout, X, 1)</span>
<span id="L27" class="LineNr">27 </span><span class="subxS1Comment"># . fd = 1 (stdout)</span>
<span id="L28" class="LineNr">28 </span>bb/copy-to-ebx  1/imm32
<span id="L29" class="LineNr">29 </span><span class="subxS1Comment"># . initialize X (location to read from)</span>
<span id="L30" class="LineNr">30 </span>b9/copy-to-ecx  <span class="SpecialChar"><a href='ex4.subx.html#L10'>X</a></span>/imm32
<span id="L31" class="LineNr">31 </span><span class="subxS1Comment"># . size = 1 character</span>
<span id="L32" class="LineNr">32 </span>ba/copy-to-edx  1/imm32
<span id="L33" class="LineNr">33 </span><span class="subxS1Comment"># . syscall</span>
<span id="L34" class="LineNr">34 </span>e8/call  syscall_write/disp32
<span id="L35" class="LineNr">35 </span>
<span id="L36" class="LineNr">36 </span><span class="subxComment"># exit(ebx)</span>
<span id="L37" class="LineNr">37 </span>e8/call  syscall_exit/disp32
<span id="L38" class="LineNr">38 </span>
<span id="L39" class="LineNr">39 </span><span class="subxS2Comment"># . . vim&#0058;nowrap:textwidth=0</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
">272 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 273 $get-num:loop-stage2: 274 # out->data[out->write] = LSB(Look) 275 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 1/index/ecx . 3/r32/ebx 0xc/disp8 . # copy edi+ecx+12 to ebx 276 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Look/disp32 . # copy *Look to eax 277 88/copy-byte 0/mod/indirect 3/rm32/ebx . . . 0/r32/AL . . # copy byte at AL to *ebx 278 # ++out->write 279 41/increment-ecx 280 # Look = get-char(in) 281 # . . push args 282 56/push-esi 283 # . . call 284 e8/call get-char/disp32 285 # . . discard args 286 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 287 # if (is-digit?(Look)) loop 288 # . eax = is-digit?(Look) 289 # . . push args 290 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look 291 # . . call 292 e8/call is-digit?/disp32 293 # . . discard args 294 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 295 # . if (eax != false) loop 296 3d/compare-eax-and 0/imm32/false 297 0f 85/jump-if-!= $get-num:loop/disp32 298 $get-num:loop-end: 299 # persist necessary variables from registers 300 89/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy ecx to *edi 301 $get-num:end: 302 # . restore registers 303 5f/pop-to-edi 304 5e/pop-to-esi 305 5b/pop-to-ebx 306 5a/pop-to-edx 307 59/pop-to-ecx 308 58/pop-to-eax 309 # . epilogue 310 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 311 5d/pop-to-ebp 312 c3/return 313 314 test-get-num-reads-single-digit: 315 # - check that get-num returns first character if it's a digit 316 # This test uses exit-descriptors. Use ebp for setting up local variables. 317 55/push-ebp 318 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 319 # clear all streams 320 # . clear-stream(_test-stream) 321 # . . push args 322 68/push _test-stream/imm32 323 # . . call 324 e8/call clear-stream/disp32 325 # . . discard args 326 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 327 # . clear-stream($_test-buffered-file->buffer) 328 # . . push args 329 68/push $_test-buffered-file->buffer/imm32 330 # . . call 331 e8/call clear-stream/disp32 332 # . . discard args 333 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 334 # . clear-stream(_test-output-stream) 335 # . . push args 336 68/push _test-output-stream/imm32 337 # . . call 338 e8/call clear-stream/disp32 339 # . . discard args 340 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 341 # . clear-stream(_test-error-stream) 342 # . . push args 343 68/push _test-error-stream/imm32 344 # . . call 345 e8/call clear-stream/disp32 346 # . . discard args 347 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 348 # initialize 'in' 349 # . write(_test-stream, "3") 350 # . . push args 351 68/push "3"/imm32 352 68/push _test-stream/imm32 353 # . . call 354 e8/call write/disp32 355 # . . discard args 356 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 357 # initialize exit-descriptor 'ed' for the call to 'get-num' below 358 # . var ed/eax : exit-descriptor 359 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 360 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax 361 # . tailor-exit-descriptor(ed, 16) 362 # . . push args 363 68/push 0x10/imm32/nbytes-of-args-for-get-num 364 50/push-eax/ed 365 # . . call 366 e8/call tailor-exit-descriptor/disp32 367 # . . discard args 368 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 369 # prime the pump 370 # . get-char(_test-buffered-file) 371 # . . push args 372 68/push _test-buffered-file/imm32 373 # . . call 374 e8/call get-char/disp32 375 # . . discard args 376 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 377 # get-num(in, out, err, ed) 378 # . . push args 379 50/push-eax/ed 380 68/push _test-error-stream/imm32 381 68/push _test-output-stream/imm32 382 68/push _test-buffered-file/imm32 383 # . . call 384 e8/call get-num/disp32 385 # registers except esp may be clobbered at this point 386 # . . discard args 387 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 388 # check-ints-equal(*_test-output-stream->data, '3', msg) 389 # . . push args 390 68/push "F - test-get-num-reads-single-digit"/imm32 391 68/push 0x33/imm32 392 b8/copy-to-eax _test-output-stream/imm32 393 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) 394 # . . call 395 e8/call check-ints-equal/disp32 396 # . . discard args 397 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 398 # . reclaim locals 399 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 400 5d/pop-to-ebp 401 c3/return 402 403 test-get-num-aborts-on-non-digit-in-Look: 404 # - check that get-num returns first character if it's a digit 405 # This test uses exit-descriptors. Use ebp for setting up local variables. 406 55/push-ebp 407 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 408 # clear all streams 409 # . clear-stream(_test-stream) 410 # . . push args 411 68/push _test-stream/imm32 412 # . . call 413 e8/call clear-stream/disp32 414 # . . discard args 415 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 416 # . clear-stream($_test-buffered-file->buffer) 417 # . . push args 418 68/push $_test-buffered-file->buffer/imm32 419 # . . call 420 e8/call clear-stream/disp32 421 # . . discard args 422 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 423 # . clear-stream(_test-output-stream) 424 # . . push args 425 68/push _test-output-stream/imm32 426 # . . call 427 e8/call clear-stream/disp32 428 # . . discard args 429 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 430 # . clear-stream(_test-error-stream) 431 # . . push args 432 68/push _test-error-stream/imm32 433 # . . call 434 e8/call clear-stream/disp32 435 # . . discard args 436 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 437 # initialize 'in' 438 # . write(_test-stream, "3") 439 # . . push args 440 68/push "3"/imm32 441 68/push _test-stream/imm32 442 # . . call 443 e8/call write/disp32 444 # . . discard args 445 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 446 # initialize exit-descriptor 'ed' for the call to 'get-num' below 447 # . var ed/eax : exit-descriptor 448 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 449 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax 450 # . tailor-exit-descriptor(ed, 16) 451 # . . push args 452 68/push 0x10/imm32/nbytes-of-args-for-get-num 453 50/push-eax/ed 454 # . . call 455 e8/call tailor-exit-descriptor/disp32 456 # . . discard args 457 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 458 # *don't* prime the pump 459 # get-num(in, out, err, ed) 460 # . . push args 461 50/push-eax/ed 462 68/push _test-error-stream/imm32 463 68/push _test-output-stream/imm32 464 68/push _test-buffered-file/imm32 465 # . . call 466 e8/call get-num/disp32 467 # registers except esp may be clobbered at this point 468 # . . discard args 469 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 470 # check that get-num tried to call exit(1) 471 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 472 # . . push args 473 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 474 68/push 2/imm32 475 # . . push ed->value 476 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) 477 # . . call 478 e8/call check-ints-equal/disp32 479 # . . discard args 480 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 481 # . reclaim locals 482 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 483 5d/pop-to-ebp 484 c3/return 485 486 test-get-num-reads-multiple-digits: 487 # - check that get-num returns all initial digits until it encounters a non-digit 488 # This test uses exit-descriptors. Use ebp for setting up local variables. 489 55/push-ebp 490 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 491 # clear all streams 492 # . clear-stream(_test-stream) 493 # . . push args 494 68/push _test-stream/imm32 495 # . . call 496 e8/call clear-stream/disp32 497 # . . discard args 498 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 499 # . clear-stream($_test-buffered-file->buffer) 500 # . . push args 501 68/push $_test-buffered-file->buffer/imm32 502 # . . call 503 e8/call clear-stream/disp32 504 # . . discard args 505 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 506 # . clear-stream(_test-output-stream) 507 # . . push args 508 68/push _test-output-stream/imm32 509 # . . call 510 e8/call clear-stream/disp32 511 # . . discard args 512 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 513 # . clear-stream(_test-error-stream) 514 # . . push args 515 68/push _test-error-stream/imm32 516 # . . call 517 e8/call clear-stream/disp32 518 # . . discard args 519 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 520 # initialize 'in' 521 # . write(_test-stream, "3456 x") 522 # . . push args 523 68/push "3456"/imm32 524 68/push _test-stream/imm32 525 # . . call 526 e8/call write/disp32 527 # . . discard args 528 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 529 # initialize exit-descriptor 'ed' for the call to 'get-num' below 530 # . var ed/eax : exit-descriptor 531 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 532 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax 533 # . tailor-exit-descriptor(ed, 16) 534 # . . push args 535 68/push 0x10/imm32/nbytes-of-args-for-get-num 536 50/push-eax/ed 537 # . . call 538 e8/call tailor-exit-descriptor/disp32 539 # . . discard args 540 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 541 # prime the pump 542 # . get-char(_test-buffered-file) 543 # . . push args 544 68/push _test-buffered-file/imm32 545 # . . call 546 e8/call get-char/disp32 547 # . . discard args 548 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 549 # get-num(in, out, err, ed) 550 # . . push args 551 50/push-eax/ed 552 68/push _test-error-stream/imm32 553 68/push _test-output-stream/imm32 554 68/push _test-buffered-file/imm32 555 # . . call 556 e8/call get-num/disp32 557 # registers except esp may be clobbered at this point 558 # . . discard args 559 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 560 # check-ints-equal(*_test-output-stream->data, '3456', msg) 561 # . . push args 562 68/push "F - test-get-num-reads-multiple-digits"/imm32 563 68/push 0x36353433/imm32 564 b8/copy-to-eax _test-output-stream/imm32 565 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) 566 # . . call 567 e8/call check-ints-equal/disp32 568 # . . discard args 569 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 570 # . reclaim locals 571 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 572 5d/pop-to-ebp 573 c3/return 574 575 test-get-num-reads-multiple-digits-followed-by-nondigit: 576 # - check that get-num returns all initial digits until it encounters a non-digit 577 # This test uses exit-descriptors. Use ebp for setting up local variables. 578 55/push-ebp 579 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 580 # clear all streams 581 # . clear-stream(_test-stream) 582 # . . push args 583 68/push _test-stream/imm32 584 # . . call 585 e8/call clear-stream/disp32 586 # . . discard args 587 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 588 # . clear-stream($_test-buffered-file->buffer) 589 # . . push args 590 68/push $_test-buffered-file->buffer/imm32 591 # . . call 592 e8/call clear-stream/disp32 593 # . . discard args 594 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 595 # . clear-stream(_test-output-stream) 596 # . . push args 597 68/push _test-output-stream/imm32 598 # . . call 599 e8/call clear-stream/disp32 600 # . . discard args 601 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 602 # . clear-stream(_test-error-stream) 603 # . . push args 604 68/push _test-error-stream/imm32 605 # . . call 606 e8/call clear-stream/disp32 607 # . . discard args 608 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 609 # initialize 'in' 610 # . write(_test-stream, "3456 x") 611 # . . push args 612 68/push "3456 x"/imm32 613 68/push _test-stream/imm32 614 # . . call 615 e8/call write/disp32 616 # . . discard args 617 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 618 # initialize exit-descriptor 'ed' for the call to 'get-num' below 619 # . var ed/eax : exit-descriptor 620 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp 621 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax 622 # . tailor-exit-descriptor(ed, 16) 623 # . . push args 624 68/push 0x10/imm32/nbytes-of-args-for-get-num 625 50/push-eax/ed 626 # . . call 627 e8/call tailor-exit-descriptor/disp32 628 # . . discard args 629 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 630 # prime the pump 631 # . get-char(_test-buffered-file) 632 # . . push args 633 68/push _test-buffered-file/imm32 634 # . . call 635 e8/call get-char/disp32 636 # . . discard args 637 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 638 # get-num(in, out, err, ed) 639 # . . push args 640 50/push-eax/ed 641 68/push _test-error-stream/imm32 642 68/push _test-output-stream/imm32 643 68/push _test-buffered-file/imm32 644 # . . call 645 e8/call get-num/disp32 646 # registers except esp may be clobbered at this point 647 # . . discard args 648 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 649 # check-ints-equal(*_test-output-stream->data, '3456', msg) 650 # . . push args 651 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 652 68/push 0x36353433/imm32 653 b8/copy-to-eax _test-output-stream/imm32 654 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) 655 # . . call 656 e8/call check-ints-equal/disp32 657 # . . discard args 658 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 659 # . reclaim locals 660 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 661 5d/pop-to-ebp 662 c3/return 663 664 ## helpers 665 666 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) 667 expected: # ed : (addr exit-descriptor), f : fd or (addr stream byte), s : (addr array byte) 668 # . prologue 669 55/push-ebp 670 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 671 # write(f, "Error: ") 672 # . . push args 673 68/push "Error: "/imm32 674 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 675 # . . call 676 e8/call write/disp32 677 # . . discard args 678 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 679 # write(f, s) 680 # . . push args 681 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 682 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 683 # . . call 684 e8/call write/disp32 685 # . . discard args 686 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 687 # write(f, " expected\n") 688 # . . push args 689 68/push " expected\n"/imm32 690 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 691 # . . call 692 e8/call write/disp32 693 # . . discard args 694 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 695 # stop(ed, 1) 696 # . . push args 697 68/push 1/imm32 698 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 699 # . . call 700 e8/call stop/disp32 701 # should never get past this point 702 $expected:dead-end: 703 # . epilogue 704 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 705 5d/pop-to-ebp 706 c3/return 707 708 # read a byte from 'f', and save it in 'Look' 709 get-char: # f : (addr buffered-file) 710 # . prologue 711 55/push-ebp 712 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 713 # . save registers 714 50/push-eax 715 # eax = read-byte-buffered(f) 716 # . . push args 717 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 718 # . . call 719 e8/call read-byte-buffered/disp32 720 # . . discard args 721 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 722 # save eax to Look 723 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Look/disp32 . # copy eax to *Look 724 $get-char:end: 725 # . restore registers 726 58/pop-to-eax 727 # . epilogue 728 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 729 5d/pop-to-ebp 730 c3/return 731 732 is-digit?: # c : int -> eax : boolean 733 # . prologue 734 55/push-ebp 735 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 736 # eax = false 737 b8/copy-to-eax 0/imm32 738 # if (c < '0') return false 739 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x30/imm32 # compare *(ebp+8) 740 7c/jump-if-< $is-digit?:end/disp8 741 # if (c > '9') return false 742 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x39/imm32 # compare *(ebp+8) 743 7f/jump-if-> $is-digit?:end/disp8 744 # otherwise return true 745 b8/copy-to-eax 1/imm32 746 $is-digit?:end: 747 # . epilogue 748 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 749 5d/pop-to-ebp 750 c3/return 751 752 == data 753 754 Look: # (char with some extra padding) 755 0/imm32 756 757 # . . vim:nowrap:textwidth=0