https://github.com/akkartik/mu/blob/master/subx/054string-equal.subx
  1 # Comparing 'regular' length-prefixed strings.
  2 
  3 == code
  4 #   instruction                     effective address                                                   register    displacement    immediate
  5 # . op          subop               mod             rm32          base        index         scale       r32
  6 # . 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
  7 
  8 # main:
  9     # run-tests()
 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 11     # syscall(exit, Num-test-failures)
 12     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 13     b8/copy-to-EAX  1/imm32/exit
 14     cd/syscall  0x80/imm8
 15 
 16 string-equal:  # s : string, benchmark : string -> EAX : boolean
 17     # pseudocode:
 18     #   if s->length != b->length return false
 19     #   for i = 0;  i < s->length;  ++i
 20     #     if s[i] != b[i] return false
 21     #   return true
 22     # registers:
 23     #   i: ECX
 24     #   s->length: EDX
 25     #   b->length: EBX
 26     #   b[i]: EBX
 27     #   s[i]: EAX
 28     #
 29     # . prolog
 30     55/push-EBP
 31     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 32     # . save registers
 33     51/push-ECX
 34     52/push-EDX
 35     53/push-EBX
 36     56/push-ESI
 37     # var s/EAX : (address array byte)
 38     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 39     # var benchmark/EBX : (address array byte)
 40     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
 41     # if s->length != b->length return false
 42     # EDX = s->length
 43     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy *EAX to EDX
 44     # compare s->length and b->length
 45     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare *EBX with EDX
 46     75/jump-if-not-equal  $string-equal:false/disp8
 47 $string-equal:lengths:
 48     # var i/ECX : int = 0
 49     b9/copy-to-ECX  0/imm32
 50     # EBX = &b[i]
 51     43/inc-EBX
 52     # EAX = &s[i]
 53     40/inc-EAX
 54 $string-equal:loop:
 55     # if i >= s->length return true
 56     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
 57     7d/jump-if-greater-or-equal  $string-equal:true/disp8
 58     # if b[i] != s[i] return false
 59     # ESI = s[i]
 60     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy *EAX to ESI
 61     # compare b[i] with ESI
 62     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # compare *EBX with ESI
 63     75/jump-if-not-equal  $string-equal:false/disp8
 64     # ++i
 65     41/inc-ECX
 66     40/inc-EAX
 67     43/inc-EBX
 68     # loop
 69     eb/jump  $string-equal:loop/disp8
 70 $string-equal:true:
 71     # return true
 72     b8/copy-to-EAX  1/imm32
 73     eb/jump  $string-equal:end/disp8
 74 $string-equal:false:
 75     # return false
 76     b8/copy-to-EAX  0/imm32
 77 $string-equal:end:
 78     # . restore registers
 79     5e/pop-to-ESI
 80     5b/pop-to-EBX
 81     5a/pop-to-EDX
 82     59/pop-to-ECX
 83     # . epilog
 84     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 85     5d/pop-to-EBP
 86     c3/return
 87 
 88 # - tests
 89 
 90 test-compare-empty-with-empty-string:
 91     # EAX = string-equal("", "")
 92     # . . push args
 93     68/push  ""/imm32
 94     68/push  ""/imm32
 95     # . . call
 96     e8/call  string-equal/disp32
 97     # . . discard args
 98     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 99     # check-ints-equal(EAX, 1, msg)
100     # . . push args
101     68/push  "F - test-compare-empty-with-empty-string"/imm32
102     68/push  1/imm32/true
103     50/push-EAX
104     # . . call
105     e8/call  check-ints-equal/disp32
106     # . . discard args
107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
108     c3/return
109 
110 test-compare-empty-with-non-empty-string:  # also checks length-mismatch code path
111     # EAX = string-equal("", "Abc")
112     # . . push args
113     68/push  "Abc"/imm32
114     68/push  ""/imm32
115     # . . call
116     e8/call  string-equal/disp32
117     # . . discard args
118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
119     # check-ints-equal(EAX, 0, msg)
120     # . . push args
121     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
122     68/push  0/imm32/false
123     50/push-EAX
124     # . . call
125     e8/call  check-ints-equal/disp32
126     # . . discard args
127     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
128     c3/return
129 
130 test-compare-equal-strings:
131     # EAX = string-equal("Abc", "Abc")
132     # . . push args
133     68/push  "Abc"/imm32
134     68/push  "Abc"/imm32
135     # . . call
136     e8/call  string-equal/disp32
137     # . . discard args
138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
139     # check-ints-equal(EAX, 1, msg)
140     # . . push args
141     68/push  "F - test-compare-equal-strings"/imm32
142     68/push  1/imm32/true
143     50/push-EAX
144     # . . call
145     e8/call  check-ints-equal/disp32
146     # . . discard args
147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
148     c3/return
149 
150 test-compare-inequal-strings-equal-lengths:
151     # EAX = string-equal("Abc", "Adc")
152     # . . push args
153     68/push  "Adc"/imm32
154     68/push  "Abc"/imm32
155     # . . call
156     e8/call  string-equal/disp32
157     # . . discard args
158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
159     # check-ints-equal(EAX, 0, msg)
160     # . . push args
161     68/push  "F - test-compare-inequal-strings-equal-lengths"/imm32
162     68/push  0/imm32/false
163     50/push-EAX
164     # . . call
165     e8/call  check-ints-equal/disp32
166     # . . discard args
167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
168     c3/return
169 
170 # . . vim:nowrap:textwidth=0