about summary refs log tree commit diff stats
path: root/075print-int-decimal.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-09-19 21:01:43 -0700
committerKartik Agaram <vc@akkartik.com>2019-09-19 23:25:49 -0700
commitfd91f7f61bfa84cbc24590d5394d75891cc1cfcc (patch)
tree6cbaebc23ed7c3b8627dac22e7054f14c4d38fe9 /075print-int-decimal.subx
parent881c7f02708ceaef54a0d8b5fcc136a3003d3e09 (diff)
downloadmu-fd91f7f61bfa84cbc24590d5394d75891cc1cfcc.tar.gz
5675 - move helpers from subx-common into layers
This undoes 5672 in favor of a new plan:

Layers 000 - 099 are for running without syntax sugar. We use them for
building syntax-sugar passes.

Layers 100 and up are for running with all syntax sugar.

The layers are arranged in approximate order so more phases rely on earlier
layers than later ones.

I plan to not use intermediate syntax sugar (just sigils without calls,
or sigils and calls without braces) anywhere except in the specific passes
implementing them.
Diffstat (limited to '075print-int-decimal.subx')
-rw-r--r--075print-int-decimal.subx307
1 files changed, 307 insertions, 0 deletions
diff --git a/075print-int-decimal.subx b/075print-int-decimal.subx
new file mode 100644
index 00000000..f7898fe9
--- /dev/null
+++ b/075print-int-decimal.subx
@@ -0,0 +1,307 @@
+# Helper to print an int32 in decimal.
+
+== code
+#   instruction                     effective address                                                   register    displacement    immediate
+# . op          subop               mod             rm32          base        index         scale       r32
+# . 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
+
+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:
+    #   push sentinel
+    #   eax = abs(n)
+    #   while true
+    #     sign-extend eax into edx
+    #     eax, edx = eax/10, eax%10
+    #     edx += '0'
+    #     push edx
+    #     if (eax == 0) break
+    #   if n < 0
+    #     push '-'
+    #   w = out->write
+    #   curr = &out->data[out->write]
+    #   max = &out->data[out->length]
+    #   while true
+    #     pop into eax
+    #     if (eax == sentinel) break
+    #     if (curr >= max) abort
+    #     *curr = AL
+    #     ++curr
+    #     ++w
+    #   out->write = w
+    # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa)
+    # (this pseudocode contains registers because operations like division
+    # require specific registers in x86)
+    #
+    # . prolog
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # ten/ecx = 10
+    b9/copy-to-ecx  0xa/imm32
+    # push sentinel
+    68/push  0/imm32/sentinel
+    # eax = abs(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:
+    # eax, edx = eax / 10, eax % 10
+    99/sign-extend-eax-into-edx
+    f7          7/subop/idiv        3/mod/direct    1/rm32/ecx    .           .             .           .           .               .                 # divide edx:eax by ecx, storing quotient in eax and remainder in edx
+    # edx += '0'
+    81          0/subop/add         3/mod/direct    2/rm32/edx    .           .             .           .           .               0x30/imm32        # add to edx
+    # push edx
+    52/push-edx
+    # if (eax == 0) break
+    3d/compare-eax-and  0/imm32
+    7f/jump-if-greater  $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  0x2d/imm32/-
+$print-int32-decimal:write:
+    # edi = out
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
+    # w/edx = out->write
+    8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy *edi to edx
+    # curr/ecx = &out->data[out->write]
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           1/r32/ecx   0xc/disp8       .                 # copy ebx+edx+12 to ecx
+    # max/ebx = &out->data[out->length]
+    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   8/disp8         .                 # copy *(edi+8) to ebx
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  3/index/ebx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+ebx+12 to ebx
+$print-int32-decimal:write-loop:
+    # pop into eax
+    58/pop-to-eax
+    # if (eax == sentinel) break
+    3d/compare-eax-and  0/imm32/sentinel
+    74/jump-if-equal  $print-int32-decimal:write-break/disp8
+    # if (curr >= max) abort
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
+    73/jump-if-greater-or-equal-unsigned  $print-int32-decimal:abort/disp8
+$print-int32-decimal:write-char:
+    # *curr = AL
+    88/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy AL to byte at *ecx
+    # ++curr
+    41/increment-ecx
+    # ++w
+    42/increment-edx
+    eb/jump  $print-int32-decimal:write-loop/disp8
+$print-int32-decimal:write-break:
+    # out->write = w
+    89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy edx to *edi
+$print-int32-decimal:end:
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$print-int32-decimal:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "print-int32-decimal: out of space\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall(exit, 1)
+    bb/copy-to-ebx  1/imm32
+    b8/copy-to-eax  1/imm32/exit
+    cd/syscall  0x80/imm8
+    # never gets here
+
+test-print-int32-decimal:
+    # - check that a single-digit number converts correctly
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # print-int32-decimal(_test-stream, 9)
+    # . . push args
+    68/push  9/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  print-int32-decimal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-stream, "9", msg)
+    # . . push args
+    68/push  "F - test-print-int32-decimal"/imm32
+    68/push  "9"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . end
+    c3/return
+
+test-print-int32-decimal-zero:
+    # - check that 0 converts correctly
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # print-int32-decimal(_test-stream, 0)
+    # . . push args
+    68/push  0/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  print-int32-decimal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-stream, "0", msg)
+    # . . push args
+    68/push  "F - test-print-int32-decimal-zero"/imm32
+    68/push  "0"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . end
+    c3/return
+
+test-print-int32-decimal-multiple-digits:
+    # - check that a multi-digit number converts correctly
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # print-int32-decimal(_test-stream, 10)
+    # . . push args
+    68/push  0xa/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  print-int32-decimal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-stream, "10", msg)
+    # . . push args
+    68/push  "F - test-print-int32-decimal-multiple-digits"/imm32
+    68/push  "10"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . end
+    c3/return
+
+test-print-int32-decimal-negative:
+    # - check that a negative single-digit number converts correctly
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # print-int32-decimal(_test-stream, -9)
+    # . . push args
+    68/push  -9/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  print-int32-decimal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # dump _test-stream {{{
+#?     # . write(2/stderr, "^")
+#?     # . . push args
+#?     68/push  "^"/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # . write-stream(2/stderr, _test-stream)
+#?     # . . push args
+#?     68/push  _test-stream/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write-stream/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # . write(2/stderr, "$\n")
+#?     # . . push args
+#?     68/push  "$\n"/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # }}}
+    # check-stream-equal(_test-stream, "-9", msg)
+    # . . push args
+    68/push  "F - test-print-int32-decimal-negative"/imm32
+    68/push  "-9"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . end
+    c3/return
+
+test-print-int32-decimal-negative-multiple-digits:
+    # - check that a multi-digit number converts correctly
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # print-int32-decimal(_test-stream, -10)
+    # . . push args
+    68/push  -0xa/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  print-int32-decimal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-stream, "-10", msg)
+    # . . push args
+    68/push  "F - test-print-int32-decimal-negative-multiple-digits"/imm32
+    68/push  "-10"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # . end
+    c3/return
+
+# . . vim:nowrap:textwidth=0