about summary refs log tree commit diff stats
path: root/subx/examples/ex10.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-09-21 16:33:57 -0700
committerKartik Agaram <vc@akkartik.com>2018-09-21 16:38:35 -0700
commitddeed58f8524849315286de8ff825c43e5b82f9d (patch)
tree682bc948d7fa55d587a06e56b43e941d3fce8524 /subx/examples/ex10.subx
parent45967d2106335dbd595cb43ae5732ba88b5d0553 (diff)
downloadmu-ddeed58f8524849315286de8ff825c43e5b82f9d.tar.gz
4579
New example program: ascii null-terminated string comparison

I'd hoped this would be a stepping stone to supporting general ascii comparison,
but we're planning to use size-prefixed rather than null-terminated arrays
everywhere. The only exception is commandline arguments, which will remain
null-terminated to interoperate with Linux.

So I'm going to need separate functions for "compare with argv" and for
general string comparison.
Diffstat (limited to 'subx/examples/ex10.subx')
-rw-r--r--subx/examples/ex10.subx67
1 files changed, 67 insertions, 0 deletions
diff --git a/subx/examples/ex10.subx b/subx/examples/ex10.subx
new file mode 100644
index 00000000..1b0b0a29
--- /dev/null
+++ b/subx/examples/ex10.subx
@@ -0,0 +1,67 @@
+## String comparison: return 1 iff the two args passed in at the commandline are equal.
+#
+# To run:
+#   $ subx translate ex10.subx ex10
+#   $ subx run ex10 abc abd
+# Expected result:
+#   $ echo $?
+#   0  # false
+
+== code
+# instruction                     effective address                                                   operand     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
+
+# main: return argv_equal(argv[1], argv[2])
+#       At the start of a SubX program:
+#         argc: *ESP
+#         argv[0]: *(ESP+4)
+#         argv[1]: *(ESP+8)
+#         ...
+  # s1 = argv[1] (EAX)
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   8/disp8         .                 # copy *(ESP+8) to EAX
+  # s2 = argv[2] (EBX)
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           3/r32/EBX   0xc/disp8       .                 # copy *(ESP+12) to EBX
+  # call argv_equal(s1, s2)
+    # push args
+  50/push                         .               .             .           .             .           .           .               .                 # push EAX
+  53/push                         .               .             .           .             .           .           .               .                 # push EBX
+    # call
+  e8/call argv_equal/disp32
+  # exit(EAX)
+  89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+  b8/copy                         .               .             .           .             .           .           .               1/imm32           # copy 1 to EAX
+  cd/syscall                      .               .             .           .             .           .           .               0x80/imm8         # int 80h
+
+# compare two null-terminated ascii strings
+# reason for the name: the only place we should have null-terminated ascii strings is from commandline args
+argv_equal:  # (s1, s2) : null-terminated ascii strings -> EAX : boolean
+  # initialize s1 (ECX) and s2 (EDX)
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   8/disp8         .                 # copy *(ESP+8) to ECX
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           2/r32/EDX   4/disp8         .                 # copy *(ESP+4) to EDX
+  # while (true)
+$argv_loop:
+    # c1/EAX, c2/EBX = *s1, *s2
+  8a/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy byte at *ECX to lower byte of EAX
+  8a/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy byte at *EDX to lower byte of EBX
+    # if (c1 == 0) break
+  3d/compare                      .               .             .           .             .           .           .               0/imm32           # compare EAX with 0
+  74/jump-if-equal  $argv_break/disp8
+    # if (c1 != c2) return false
+  39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX with EBX
+  75/jump-if-not-equal  $argv_fail/disp8
+    # ++s1, ++s2
+  41/inc-ECX
+  42/inc-EDX
+  # end while
+  eb/jump  $argv_loop/disp8
+$argv_break:
+  # if (c2 == 0) return true
+  81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX with 0
+  75/jump-if-not-equal  $argv_fail/disp8
+  b8/copy                         .               .             .           .             .           .           .               1/imm32           # copy 1 to EAX
+  c3/return
+  # return false
+$argv_fail:
+  b8/copy                         .               .             .           .             .           .           .               0/imm32           # copy 0 to EAX
+  c3/return