https://github.com/akkartik/mu/blob/master/subx/apps/subx-common.subx
  1 # some common helpers shared by phases of the SubX converter
  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 # (re)compute the bounds of the next word in the line
  9 # return empty string on reaching end of file
 10 next-word:  # line : (address stream byte), out : (address slice)
 11     # . prolog
 12     55/push-EBP
 13     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 14     # . save registers
 15     50/push-EAX
 16     51/push-ECX
 17     56/push-ESI
 18     57/push-EDI
 19     # ESI = line
 20     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 21     # EDI = out
 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
 23     # skip-chars-matching(line, ' ')
 24     # . . push args
 25     68/push  0x20/imm32/space
 26     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 27     # . . call
 28     e8/call  skip-chars-matching/disp32
 29     # . . discard args
 30     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 31 $next-word:check0:
 32     # if (line->read >= line->write) clear out and return
 33     # . EAX = line->read
 34     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
 35     # . if (EAX < line->write) goto next check
 36     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
 37     7c/jump-if-lesser  $next-word:check1/disp8
 38     # . return out = {0, 0}
 39     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
 40     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
 41     eb/jump  $next-word:end/disp8
 42 $next-word:check1:
 43     # out->start = &line->data[line->read]
 44     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 45     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
 46     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
 47     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
 48     # . EAX = line->data[line->read]
 49     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 50     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
 51     # . compare
 52     3d/compare-EAX-and  0x23/imm32/pound
 53     75/jump-if-not-equal  $next-word:not-comment/disp8
 54     # . out->end = &line->data[line->write]
 55     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
 56     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
 57     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 58     # . line->read = line->write
 59     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
 60     # . return
 61     eb/jump  $next-word:end/disp8
 62 $next-word:not-comment:
 63     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
 64     # . . push args
 65     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 66     # . . call
 67     e8/call  skip-chars-not-matching-whitespace/disp32
 68     # . . discard args
 69     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 70     # out->end = &line->data[line->read]
 71     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 72     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
 73     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 74 $next-word:end:
 75     # . restore registers
 76     5f/pop-to-EDI
 77     5e/pop-to-ESI
 78     59/pop-to-ECX
 79     58/pop-to-EAX
 80     # . epilog
 81     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 82     5d/pop-to-EBP
 83     c3/return
 84 
 85 test-next-word:
 86     # . prolog
 87     55/push-EBP
 88     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 89     # setup
 90     # . clear-stream(_test-stream)
 91     # . . push args
 92     68/push  _test-stream/imm32
 93     # . . call
 94     e8/call  clear-stream/disp32
 95     # . . discard args
 96     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 97     # var slice/ECX = {0, 0}
 98     68/push  0/imm32/end
 99     68/push  0/imm32/start
100     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
101     # write(_test-stream, "  ab")
102     # . . push args
103     68/push  "  ab"/imm32
104     68/push  _test-stream/imm32
105     # . . call
106     e8/call  write/disp32
107     # . . discard args
108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
109     # next-word(_test-stream, slice)
110     # . . push args
111     51/push-ECX
112     68/push  _test-stream/imm32
113     # . . call
114     e8/call  next-word/disp32
115     # . . discard args
116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
117     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
118     # . check-ints-equal(slice->start - _test-stream, 14, msg)
119     # . . push args
120     68/push  "F - test-next-word: start"/imm32
121     68/push  0xe/imm32
122     # . . push slice->start - _test-stream
123     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
124     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
125     50/push-EAX
126     # . . call
127     e8/call  check-ints-equal/disp32
128     # . . discard args
129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
130     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
131     # . check-ints-equal(slice->end - _test-stream, 16, msg)
132     # . . push args
133     68/push  "F - test-next-word: end"/imm32
134     68/push  0x10/imm32
135     # . . push slice->end - _test-stream
136     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
137     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
138     50/push-EAX
139     # . . call
140     e8/call  check-ints-equal/disp32
141     # . . discard args
142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
143     # . epilog
144     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
145     5d/pop-to-EBP
146     c3/return
147 
148 test-next-word-returns-whole-comment:
149     # . prolog
150     55/push-EBP
151     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
152     # setup
153     # . clear-stream(_test-stream)
154     # . . push args
155     68/push  _test-stream/imm32
156     # . . call
157     e8/call  clear-stream/disp32
158     # . . discard args
159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
160     # var slice/ECX = {0, 0}
161     68/push  0/imm32/end
162     68/push  0/imm32/start
163     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
164     # write(_test-stream, "  # a")
165     # . . push args
166     68/push  "  # a"/imm32
167     68/push  _test-stream/imm32
168     # . . call
169     e8/call  write/disp32
170     # . . discard args
171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
172     # next-word(_test-stream, slice)
173     # . . push args
174     51/push-ECX
175     68/push  _test-stream/imm32
176     # . . call
177     e8/call  next-word/disp32
178     # . . discard args
179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
180     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
181     # . check-ints-equal(slice->start - _test-stream, 14, msg)
182     # . . push args
183     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
184     68/push  0xe/imm32
185     # . . push slice->start - _test-stream
186     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
187     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
188     50/push-EAX
189     # . . call
190     e8/call  check-ints-equal/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
193     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
194     # . check-ints-equal(slice->end - _test-stream, 17, msg)
195     # . . push args
196     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
197     68/push  0x11/imm32
198     # . . push slice->end - _test-stream
199     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
200     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
201     50/push-EAX
202     # . . call
203     e8/call  check-ints-equal/disp32
204     # . . discard args
205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
206     # . epilog
207     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
208     5d/pop-to-EBP
209     c3/return
210 
211 test-next-word-returns-empty-string-on-eof:
212     # . prolog
213     55/push-EBP
214     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
215     # setup
216     # . clear-stream(_test-stream)
217     # . . push args
218     68/push  _test-stream/imm32
219     # . . call
220     e8/call  clear-stream/disp32
221     # . . discard args
222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
223     # var slice/ECX = {0, 0}
224     68/push  0/imm32/end
225     68/push  0/imm32/start
226     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
227     # write nothing to _test-stream
228     # next-word(_test-stream, slice)
229     # . . push args
230     51/push-ECX
231     68/push  _test-stream/imm32
232     # . . call
233     e8/call  next-word/disp32
234     # . . discard args
235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
236     # check-ints-equal(slice->end - slice->start, 0, msg)
237     # . . push args
238     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
239     68/push  0/imm32
240     # . . push slice->end - slice->start
241     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
242     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
243     50/push-EAX
244     # . . call
245     e8/call  check-ints-equal/disp32
246     # . . discard args
247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
248     # . epilog
249     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
250     5d/pop-to-EBP
251     c3/return