about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <github@akkartik.com>2019-05-04 08:07:34 -0700
committerGitHub <noreply@github.com>2019-05-04 08:07:34 -0700
commitced63035f10b20f8cbf7a38c4336d6e5712b29f2 (patch)
tree59d624ce0b079bbff3be40fd6c538dcdcbd8db8c
parent02684e8d7cb5292867ebcdea06879b94ba63aef4 (diff)
parent4a0b4344a3249fd48ab189c97e3638f83a8a7d1b (diff)
downloadmu-ced63035f10b20f8cbf7a38c4336d6e5712b29f2.tar.gz
Merge pull request #20 from akkartik/charles-l-print-int-decimal
exercise: reimplement print-int-decimal
-rw-r--r--subx/074print-int-decimal.subx182
1 files changed, 97 insertions, 85 deletions
diff --git a/subx/074print-int-decimal.subx b/subx/074print-int-decimal.subx
index 86015a00..e5dbaa30 100644
--- a/subx/074print-int-decimal.subx
+++ b/subx/074print-int-decimal.subx
@@ -6,38 +6,32 @@
 # . 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
 
 #? Entry:  # run a single test, while debugging
-#?     e8/call test-print-int32-decimal/disp32
+#?     e8/call test-print-int32-decimal-negative/disp32
+#?
 #?     # syscall(exit, Num-test-failures)
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     b8/copy-to-EAX  1/imm32/exit
 #?     cd/syscall  0x80/imm8
 
 print-int32-decimal:  # out : (address stream), n : int32
-    # works by generating characters from lowest to highest and pushing them
-    # to the stack, before popping them one by one into the stream
-    #
-    # pseudocode:
-    #   copy ESP to EBX
-    #   EAX = |n|
-    #   while true
-    #     if (EAX == 0) break
-    #     sign-extend EAX into EDX
-    #     EAX, EDX = EAX/10, EAX%10
-    #     push EDX
-    #   if n < 0
-    #     push '-' - '0' = -3
-    #   max/ECX = &out->data[out->length]
-    #   w/EAX = out->write
-    #   curr/EDI = &out->data[out->write]
-    #   while true
-    #     if (ESP == EBX) break
-    #     if (curr >= max) abort
-    #     pop into EDX
-    #     EDX += '0'  # convert decimal digit to ascii
-    #     *curr = DL
-    #     ++curr
-    #     ++w
-    #   out->write = w
+    # PSEUDOCODE
+    # based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa
+    # push SENTINAL
+    # if n >= 0:
+    #   goto digit-encode
+    # else:
+    #   n = -n
+    #   negative = true
+    # while true:
+    #   push n % 10 + '0'
+    #   n = n / 10
+    #   break if n == 0
+    # if negative:
+    #   push '-'
+    # while true:
+    #   EAX = pop
+    #   break if EAX == SENTINAL
+    #   out->data[out->write++] = EAX
     #
     # . prolog
     55/push-EBP
@@ -48,70 +42,88 @@ print-int32-decimal:  # out : (address stream), n : int32
     52/push-EDX
     53/push-EBX
     57/push-EDI
-    # copy ESP to EBX
-    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
-    # ten/ECX = 10
-    b9/copy-to-ECX  0xa/imm32
-    # EAX = |n|
-    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
-    3d/compare-EAX-with  0/imm32
-    7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8
-$print-int32-decimal:negative:
-    f7          3/subop/negate      3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # negate EAX
-$print-int32-decimal:read-loop:
-    # if (EAX == 0) break
+
+    # negative = false
+    bf/copy-to-EDI  0/imm32                                                                                                                           # EDI = 0
+
+    # push sentinal for $append-loop
+    68/push-sentinel   0/imm32
+
+    # EAX = n
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # EAX = *(EBP+12)
+    # if n >= 0 jump to digit-encode
     3d/compare-EAX-and  0/imm32
-    74/jump-if-equal $print-int32-decimal:read-break/disp8
-    # sign-extend
+    7d/jump-if-greater-eq $digit-encode:prelude/disp8
+    # else
+    # n = -n
+    f7  3/subop/negate              3/mod/direct    0/rm32/EAX                                                                                        # EAX = -EAX
+
+    # negative = true
+    bf/copy-to-EDI  1/imm32                                                                                                                           # EDI = 1
+
+$digit-encode:prelude:
+    # EBX = n
+    8b/copy                         3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # EBX = EAX
+
+$digit-encode-loop:
+    # EAX = EBX(n)
+    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # EAX = EBX
+    b9/copy-to-ECX    0xa/imm32
     99/sign-extend-EAX-into-EDX
-    # EAX, EDX = divide-with-remainder EAX/ten, EAX%ten
-    f7          7/subop/divide-by   3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
-    # push EDX
+    # EDX:EAX(n) / ECX(10)
+
+    # EDX, EAX = n % 10, n / 10
+    f7          7/subop/idiv        3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
+    81          0/subop/add         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0x30/imm32        # EDX += '0' (0x30)
+
+    # push n % 10 + '0'
     52/push-EDX
-    eb/jump  $print-int32-decimal:read-loop/disp8
-$print-int32-decimal:read-break:
-    # if (n < 0) push('-')
-    81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       0/imm32           # compare *(EBP+12)
-    7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8
-$print-int32-decimal:push-negative:
-    68/push  -3/imm32/dash-minus-zero
-    # fall through
-$print-int32-decimal:write:
-    # EDI = out
-    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-    # max/ECX = &out->data[out->length]
-    8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EDI+8) to ECX
-    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EDI+ECX+12 to ECX
-    # w/EAX = out->write
-    8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
-    # curr/EDI = &out->data[out->write]
-    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  0/index/EAX   .           7/r32/EDI   0xc/disp8       .                 # copy EDI+EAX+12 to EDI
-$print-int32-decimal:write-loop:
-    # if (ESP == EBX) break
-    39/compare                      3/mod/direct    4/rm32/ESP    .           .             .           3/r32/EBX   .               .                 # compare ESP and EBX
-    74/jump-if-equal  $print-int32-decimal:write-break/disp8
-    # if (curr >= max) abort
-    39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # compare EDI and ECX
-    7d/jump-if-greater-or-equal  $print-int32-decimal:abort/disp8
-    # pop into EDX
-    5a/pop-into-EDX
-    # EDX += '0'
-    81          0/subop/add         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0x30/imm32/zero   # add to EDX
-$print-int32-decimal:write-char:
-    # *curr = DL
-    88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/DL    .               .                 # copy DL to byte at *ECX
-    # ++curr
-    47/increment-EDI
-    # ++w
-    40/increment-EAX
-    eb/jump  $print-int32-decimal:write-loop/disp8
-$print-int32-decimal:write-break:
-    # out->write = w
-    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+
+    # EBX = n / 10
+    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # EBX = EAX
+
+    # loop if n / 10 > 0
+    3d/compare-EAX-and  0/imm32
+    7f/jump-if-greater $digit-encode-loop/disp8
+    # else:
+
+    8b/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # EAX = EDX
+    # . if !negative: goto append-loop
+    3d/compare-EAX-and 0/imm32
+    74/jump-if-equal   $append-loop:prelude/disp8
+    # . else
+    68/push-negative   0x2d/imm32 # push '-'
+
+$append-loop:prelude:
+    # EBX = &out
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EBP+8) to EBX
+
+$append-loop:
+    # EAX = next_character
+    58/pop-to-EAX
+
+    3d/compare-EAX                  .               .             .           .             .           .           .               0/imm32           # EAX == 0
+    74/jump-if-equal   $append-loop:break/disp8
+
+    # ECX = out->write
+    8b/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # ECX = *EBX
+
+    # out->data[out->write] = next_character
+    # . ECX = &out->data[out->write]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    3/base/EBX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EBX+ECX+12 to ECX
+    # . out->data[out->write] = EAX
+    89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/ECX  .                .                 # *ECX = EAX
+
+    # out->write++
+    ff          0/subop/increment   0/mod/indirect  3/rm32/EBX    .           .             .           .           .               .                 # (*EBX)++
+    eb/jump $append-loop/disp8
+
+$append-loop:break:
+
 $print-int32-decimal:end:
+    # . reclaim locals
     # . restore registers
-    5f/pop-to-EDI
+    57/pop-to-EDI
     5b/pop-to-EBX
     5a/pop-to-EDX
     59/pop-to-ECX
' href='#n583'>583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686