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