https://github.com/akkartik/mu/blob/master/subx/apps/tests.subx
  1 # Generate code for a new function called 'run-tests' which calls in sequence
  2 # all functions starting with 'test-'.
  3 
  4 == code
  5 #   instruction                     effective address                                                   register    displacement    immediate
  6 # . op          subop               mod             rm32          base        index         scale       r32
  7 # . 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
  8 
  9 Entry:
 10     # Heap = new-segment(Heap-size)
 11     # . . push args
 12     68/push  Heap/imm32
 13     68/push  Heap-size/imm32
 14     # . . call
 15     e8/call  new-segment/disp32
 16     # . . discard args
 17     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 18     # initialize-trace-stream(256KB)
 19     # . . push args
 20     68/push  0x40000/imm32/256KB
 21     # . . call
 22     e8/call  initialize-trace-stream/disp32
 23     # . . discard args
 24     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 25 
 26     # run tests if necessary, convert stdin if not
 27     # . prolog
 28     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 29     # initialize heap
 30     # - if argc > 1 and argv[1] == "test", then return run_tests()
 31     # . argc > 1
 32     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
 33     7e/jump-if-lesser-or-equal  $run-main/disp8
 34     # . argv[1] == "test"
 35     # . . push args
 36     68/push  "test"/imm32
 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 38     # . . call
 39     e8/call  kernel-string-equal?/disp32
 40     # . . discard args
 41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 42     # . check result
 43     3d/compare-EAX-and  1/imm32
 44     75/jump-if-not-equal  $run-main/disp8
 45     # . run-tests()
 46     e8/call  run-tests/disp32
 47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 48     eb/jump  $main:end/disp8
 49 $run-main:
 50     # - otherwise convert stdin
 51     # convert(Stdin, Stdout)
 52     # . . push args
 53     68/push  Stdout/imm32
 54     68/push  Stdin/imm32
 55     # . . call
 56     e8/call  convert/disp32
 57     # . . discard args
 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 59     # . syscall(exit, 0)
 60     bb/copy-to-EBX  0/imm32
 61 $main:end:
 62     b8/copy-to-EAX  1/imm32/exit
 63     cd/syscall  0x80/imm8
 64 
 65 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
 66     # pseudocode
 67     #   bool tests-found = false
 68     #   var line = new-stream(512, 1)
 69     #   var new-code-segment = new-stream(Segment-size, 1)
 70     #   write(new-code-segment, "\n==code\n")
 71     #   write(new-code-segment, "run-tests:\n")
 72     #   while true
 73     #     clear-stream(line)
 74     #     read-line-buffered(in, line)
 75     #     if (line->write == 0) break               # end of file
 76     #     var word-slice = next-word(line)
 77     #     if is-label?(word-slice)
 78     #       if slice-starts-with?(word-slice, "test-")
 79     #         tests-found = true
 80     #         write(new-code-segment, "  e8/call  ")
 81     #         write-slice(new-code-segment, word-slice)
 82     #         write(new-code-segment, "/disp32\n")
 83     #     rewind-stream(line)
 84     #     write-stream-data(out, line)
 85     #   if tests-found
 86     #     write(new-code-segment, "  c3/return\n")
 87     #     write-stream-data(out, new-code-segment)
 88     #   flush(out)
 89     #
 90     # . prolog
 91     55/push-EBP
 92     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 93     # . save registers
 94     50/push-EAX
 95     51/push-ECX
 96     52/push-EDX
 97     53/push-EBX
 98     57/push-EDI
 99     # var line/ECX : (address stream byte) = stream(512)
100     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
101     68/push  0x200/imm32/length
102     68/push  0/imm32/read
103     68/push  0/imm32/write
104     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
105     # var word-slice/EDX = {0, 0}
106     68/push  0/imm32/end
107     68/push  0/imm32/start
108     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
109     # tests-found?/EBX = false
110     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
111     # new-code-segment/EDI = new-stream(Heap, Segment-size, 1)
112     # . EAX = new-stream(Heap, Segment-size, 1)
113     # . . push args
114     68/push  1/imm32
115     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
116     68/push  Heap/imm32
117     # . . call
118     e8/call  new-stream/disp32
119     # . . discard args
120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
121     # . EDI = EAX
122     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
123     # write(new-code-segment, "\n== code\n")
124     # . . push args
125     68/push  "\n== code\n"/imm32
126     57/push-EDI
127     # . . call
128     e8/call  write/disp32
129     # . . discard args
130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
131     # write(new-code-segment, "run-tests:\n")
132     # . . push args
133     68/push  "run-tests:\n"/imm32
134     57/push-EDI
135     # . . call
136     e8/call  write/disp32
137     # . . discard args
138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
139 $convert:loop:
140     # clear-stream(line)
141     # . . push args
142     51/push-ECX
143     # . . call
144     e8/call  clear-stream/disp32
145     # . . discard args
146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
147     # read-line-buffered(in, line)
148     # . . push args
149     51/push-ECX
150     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
151     # . . call
152     e8/call  read-line-buffered/disp32
153     # . . discard args
154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
155 $convert:check0:
156     # if (line->write == 0) break
157     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
158     0f 84/jump-if-equal  $convert:break/disp32
159     # next-word(line, word-slice)
160     # . . push args
161     52/push-EDX
162     51/push-ECX
163     # . . call
164     e8/call  next-word/disp32
165     # . . discard args
166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
167 $convert:check-for-label:
168     # if (!is-label?(word-slice)) continue
169     # . EAX = is-label?(word-slice)
170     # . . push args
171     52/push-EDX
172     # . . call
173     e8/call  is-label?/disp32
174     # . . discard args
175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
176     # . if (EAX == 0) continue
177     3d/compare-EAX-and  0/imm32
178     74/jump-if-equal  $convert:continue/disp8
179 $convert:check-label-prefix:
180     # strip trailing ':' from word-slice
181     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
182     # if !slice-starts-with?(word-slice, "test-") continue
183     # . . push args
184     68/push  "test-"/imm32
185     52/push-EDX
186     # . . call
187     e8/call  slice-starts-with?/disp32
188     # . . discard args
189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
190     # . if (EAX == 0) break
191     3d/compare-EAX-and  0/imm32
192     74/jump-if-equal  $convert:continue/disp8
193 $convert:call-test-function:
194     # tests-found? = true
195     bb/copy-to-EBX  1/imm32/true
196     # write(new-code-segment, "  e8/call  ")
197     # . . push args
198     68/push  "  e8/call  "/imm32
199     57/push-EDI
200     # . . call
201     e8/call  write/disp32
202     # . . discard args
203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
204     # write-slice(new-code-segment, word-slice)
205     # . . push args
206     52/push-EDX
207     57/push-EDI
208     # . . call
209     e8/call  write-slice/disp32
210     # . . discard args
211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
212     # write(new-code-segment, "/disp32\n")
213     # . . push args
214     68/push  "/disp32\n"/imm32
215     57/push-EDI
216     # . . call
217     e8/call  write/disp32
218     # . . discard args
219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
220 $convert:continue:
221     # rewind-stream(line)
222     # . . push args
223     51/push-ECX
224     # . . call
225     e8/call  rewind-stream/disp32
226     # . . discard args
227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
228     # write-stream-data(out, line)
229     # . . push args
230     51/push-ECX
231     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
232     # . . call
233     e8/call  write-stream-data/disp32
234     # . . discard args
235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
236     # loop
237     e9/jump  $convert:loop/disp32
238 $convert:break:
239     # if (!tests-found?) goto end
240     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
241     74/jump-if-equal  $convert:end/disp8
242     # write(new-code-segment, "  c3/return\n")
243     # . . push args
244     68/push  "  c3/return\n"/imm32
245     57/push-EDI
246     # . . call
247     e8/call  write/disp32
248     # . . discard args
249     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
250     # write-stream-data(out, new-code-segment)
251     # . . push args
252     57/push-EDI
253     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
254     # . . call
255     e8/call  write-stream-data/disp32
256     # . . discard args
257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
258 $convert:end:
259     # flush(out)
260     # . . push args
261     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
262     # . . call
263     e8/call  flush/disp32
264     # . . discard args
265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
266     # . reclaim locals
267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
268     # . restore registers
269     5f/pop-to-EDI
270     5b/pop-to-EBX
271     5a/pop-to-EDX
272     59/pop-to-ECX
273     58/pop-to-EAX
274     # . epilog
275     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
276     5d/pop-to-EBP
277     c3/return
278 
279 # . . vim:nowrap:textwidth=0