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