1 # Helper to dynamically allocate memory on the heap.
3 # We'd like to be able to write tests for functions that allocate memory,
4 # making assertions on the precise addresses used. To achieve this we'll pass
5 # in an *allocation descriptor* to allocate from.
7 # Allocation descriptors are also useful outside of tests. Assembly and machine
8 # code are of necessity unsafe languages, and one of the most insidious kinds
9 # of bugs unsafe languages expose us to are dangling pointers to memory that
10 # has been freed and potentially even reused for something totally different.
11 # To reduce the odds of such "use after free" errors, SubX programs tend to not
12 # reclaim and reuse dynamically allocated memory. (Running out of memory is far
13 # easier to debug.) Long-running programs that want to reuse memory are mostly
14 # on their own to be careful. However, they do get one bit of help: they can
15 # carve out chunks of memory and then allocate from them manually using this
16 # very same 'allocate' helper. They just need a new allocation descriptor for
21 # Allocations are returned in a handle, which consists of an alloc-id and a payload.
22 # The alloc-id helps detect use-after-free errors.
23 Handle-size: # (addr int)
26 # A default allocation descriptor for programs to use.
27 Heap: # allocation-descriptor
29 0x02000000/imm32 # 32 MB
31 0x80000000/imm32 # 2 GB
34 0x100/imm32 # save a few alloc ids for fake handles
37 # instruction effective address register displacement immediate
38 # . op subop mod rm32 base index scale r32
39 # . 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
41 # Allocate and clear 'n' bytes of memory from an allocation-descriptor 'ad'.
42 # Abort if there isn't enough memory in 'ad'.
43 allocate: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
46 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
49 # allocate-raw(ad, n, out)
51 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
52 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
53 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
55 e8/call allocate-raw/disp32
57 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
58 # eax = out->payload + 4
59 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
60 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
64 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
67 e8/call zero-out/disp32
69 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
74 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
78 heap-bound: # -> _/eax: int
81 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
83 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Heap/disp32 . # copy *Heap to eax
86 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
90 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
91 # Abort if there isn't enough memory in 'ad'.
92 allocate-raw: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
95 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
104 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
106 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx
108 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx
109 # out->alloc-id = Next-alloc-id
110 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Next-alloc-id/disp32 # copy *Next-alloc-id to eax
111 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx
112 # out->payload = ad->curr
113 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
114 $allocate-raw:save-payload-in-eax:
115 89/copy 1/mod/*+disp8 2/rm32/edx . . . 0/r32/eax 4/disp8 . # copy eax to *(edx+4)
116 # *out->payload = Next-alloc-id
117 8b/copy 1/mod/*+disp8 2/rm32/edx . . . 7/r32/edi 4/disp8 . # copy *(edx+4) to edi
118 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 6/r32/esi Next-alloc-id/disp32 # copy *Next-alloc-id to esi
119 89/copy 0/mod/indirect 7/rm32/edi . . . 6/r32/esi . . # copy esi to *edi
120 $allocate-raw:increment-next-alloc-id:
121 # increment *Next-alloc-id
122 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # increment *Next-alloc-id
123 # check if there's enough space
124 # TODO: move this check up before any state updates when we support error recovery
125 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 3/index/ebx . 0/r32/eax 4/disp8 . # copy eax+ebx+4 to eax
126 3b/compare 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # compare eax with *(ecx+4)
127 73/jump-if->=-signed $allocate-raw:abort/disp8
128 $allocate-raw:commit:
130 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx
132 # . restore registers
140 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
145 (abort "allocate: failed")
148 test-allocate-raw-success:
151 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
152 # var ad/ecx: allocation-descriptor containing 16 bytes
153 # . var end/ecx: (addr byte)
155 # . var start/edx: (addr byte) = end - 16
156 81 5/subop/subtract %esp 0x10/imm32
158 # . ad = {start, end}
161 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
162 # var expected-payload/ebx = ad->curr
163 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
164 # var h/edx: handle = {0, 0}
167 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
168 # *Next-alloc-id = 0x34
169 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
170 # allocate-raw(ad, 3, h)
176 e8/call allocate-raw/disp32
178 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
179 # check-ints-equal(h->alloc-id, 0x34, msg)
181 68/push "F - test-allocate-raw-success: sets alloc-id in handle"/imm32
183 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
185 e8/call check-ints-equal/disp32
187 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
188 # check-ints-equal(h->payload, expected-payload, msg)
190 68/push "F - test-allocate-raw-success: sets payload in handle"/imm32
192 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
194 e8/call check-ints-equal/disp32
196 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
197 # check-ints-equal(h->payload->alloc-id, 0x34, msg)
199 68/push "F - test-allocate-raw-success: sets alloc-id in payload"/imm32
201 ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
203 e8/call check-ints-equal/disp32
205 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
206 # check-ints-equal(*Next-alloc-id, 0x35, msg)
208 68/push "F - test-allocate-raw-success: increments Next-alloc-id"/imm32
210 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
212 e8/call check-ints-equal/disp32
214 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
215 # check-ints-equal(ad->curr - expected-payload, 3 + 4 for alloc-id, msg)
217 68/push "F - test-allocate-raw-success: updates allocation descriptor"/imm32
219 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
220 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
223 e8/call check-ints-equal/disp32
225 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
227 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
229 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
231 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
235 lookup: # h: (handle _T) -> result/eax: (addr _T)
238 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
242 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
243 # ecx = handle->alloc_id
244 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
245 # if (ecx == 0) return 0
246 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0/imm32 # compare ecx
247 74/jump-if-= $lookup:end/disp8
248 # eax = handle->address (payload)
249 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
250 # if (ecx != *eax) abort
251 39/compare 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # compare *eax and ecx
252 75/jump-if-!= $lookup:abort/disp8
254 05/add-to-eax 4/imm32
256 # . restore registers
259 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
264 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "lookup failed: (" 3 0) # 3=cyan
265 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+8) 3 0)
266 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 ", " 3 0)
267 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0xc) 3 0)
268 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 ") -> " 3 0)
269 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
270 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 ". Contents of a few words starting from address 0: " 3 0)
271 b8/copy-to-eax 0/imm32
272 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 2 0)
273 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
275 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
276 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
278 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
279 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
281 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
282 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
284 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
285 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
287 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
288 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
290 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
291 (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " " 2 0)
293 (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 *eax 3 0)
300 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
301 # var ad/ebx: allocation-descriptor containing 16 bytes
302 # . var end/ecx: (addr byte)
304 # . var start/edx: (addr byte) = end - 16
305 81 5/subop/subtract %esp 0x10/imm32
307 # . ad = {start, end}
310 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
311 # var handle/ecx: handle
312 68/push 0/imm32/address
313 68/push 0/imm32/alloc-id
314 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
315 # var old-top/edx = ad->curr
316 8b/copy 0/mod/indirect 3/rm32/ebx . . . 2/r32/edx . . # copy *ebx to edx
317 # allocate-raw(ad, 2, handle)
323 e8/call allocate-raw/disp32
325 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
326 # eax = lookup(handle)
328 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
329 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
331 e8/call lookup/disp32
333 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
334 # eax contains old top of heap, except skipping the alloc-id in the payload
335 # . check-ints-equal(eax, old-top+4, msg)
337 68/push "F - test-lookup-success"/imm32
338 81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 4/imm32 # add to edx
342 e8/call check-ints-equal/disp32
344 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
346 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
348 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
350 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
354 test-lookup-null-returns-null:
357 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
358 # var handle/ecx: handle
359 68/push 0/imm32/address
360 68/push 0/imm32/alloc-id
361 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
362 # eax = lookup(handle)
364 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
365 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
367 e8/call lookup/disp32
369 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
370 # check-ints-equal(eax, 0, msg)
372 68/push "F - test-lookup-null-returns-null"/imm32
376 e8/call check-ints-equal/disp32
378 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
380 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
382 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
386 _pending-test-lookup-failure:
389 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
390 # var ad/ecx: allocation-descriptor containing 16 bytes
391 # . var end/ecx: (addr byte)
393 # . var start/edx: (addr byte) = end - 16
394 81 5/subop/subtract %esp 0x10/imm32
396 # . ad = {start, end}
399 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
401 68/push 0/imm32/address
402 68/push 0/imm32/alloc-id
403 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
404 # var old_top/ebx = ad->curr
405 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx
406 # first allocation, to h1
407 # . allocate(ad, 2, h1)
413 e8/call allocate/disp32
415 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
416 # reset ad->curr to mimic reclamation
417 89/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy ebx to *esi
418 # second allocation that returns the same address as the first
420 68/push 0/imm32/address
421 68/push 0/imm32/alloc-id
422 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
423 # . allocate(ad, 2, h2)
429 e8/call allocate/disp32
431 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
432 # check-ints-equal(h1->address, h2->address, msg)
434 68/push "F - test-lookup-failure"/imm32
435 ff 6/subop/push 1/mod/*+disp8 2/rm32/ecx . . . . 4/disp8 . # push *(edx+4)
436 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
438 e8/call check-ints-equal/disp32
440 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
441 # lookup(h1) should crash
443 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
444 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
446 e8/call lookup/disp32
447 # should never get past this point
449 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
451 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
453 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
455 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
459 # when comparing handles, just treat them as pure values
460 handle-equal?: # a: (handle _T), b: (handle _T) -> result/eax: boolean
463 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
467 b8/copy-to-eax 0/imm32/false
468 $handle-equal?:compare-alloc-id:
470 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
471 # if (ecx != b->alloc_id) return false
472 39/compare 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # compare ecx and *(ebp+16)
473 75/jump-if-!= $handle-equal?:end/disp8
474 $handle-equal?:compare-address:
475 # ecx = handle->address
476 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
477 # if (ecx != b->address) return false
478 39/compare 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x14/disp8 . # compare ecx and *(ebp+20)
479 75/jump-if-!= $handle-equal?:end/disp8
480 $handle-equal?:return-true:
482 b8/copy-to-eax 1/imm32/true
484 # . restore registers
487 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
491 copy-handle: # src: (handle _T), dest: (addr handle _T)
494 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
499 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx
501 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
502 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx
503 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
504 89/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4)
506 # . restore registers
510 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
514 # helper: create a nested allocation descriptor (useful for tests)
515 allocate-region: # ad: (addr allocation-descriptor), n: int, out: (addr handle allocation-descriptor)
518 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
522 # allocate(ad, n, out)
524 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
525 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
526 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
528 e8/call allocate/disp32
530 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
532 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
533 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
534 # skip payload->allocid
535 05/add-to-eax 4/imm32
536 # if (eax == 0) abort
537 3d/compare-eax-and 0/imm32
538 74/jump-if-= $allocate-region:abort/disp8
539 # earmark 8 bytes at the start for a new allocation descriptor
541 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx
542 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 8/imm32 # add to ecx
543 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
544 # . *(eax+4) = eax + n
545 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx
546 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # add *(ebp+12) to ecx
547 89/copy 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy ecx to *(eax+4)
548 # . restore registers
552 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
556 # We could create a more general '$abort' jump target, but then we'd need to do
557 # a conditional jump followed by loading the error message and an unconditional
558 # jump. Or we'd need to unconditionally load the error message before a
559 # conditional jump, even if it's unused the vast majority of the time. This way
560 # we bloat a potentially cold segment in RAM so we can abort with a single
562 $allocate-region:abort:
563 (abort "allocate-region: failed to allocate")
566 # Claim the next 'n+4' bytes of memory and initialize the first 4 to n.
567 # Abort if there isn't enough memory in 'ad'.
568 allocate-array: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
571 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
577 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
578 # var size/edx: int = n+4
579 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy ecx+4 to edx
580 # allocate(ad, size, out)
582 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
584 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
586 e8/call allocate/disp32
588 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
590 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
591 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
592 # . skip payload->allocid
593 05/add-to-eax 4/imm32
595 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
597 # . restore registers
602 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
609 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
610 # var ad/ecx: allocation-descriptor containing 16 bytes
611 # . var end/ecx: (addr byte)
613 # . var start/edx: (addr byte) = end - 16
614 81 5/subop/subtract %esp 0x10/imm32
616 # . ad = {start, end}
619 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
620 # var expected-payload/ebx = ad->curr
621 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
622 # var h/edx: handle = {0, 0}
625 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
626 # *Next-alloc-id = 0x34
627 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
628 # allocate-array(ad, 3, h)
634 e8/call allocate-array/disp32
636 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
637 # check-ints-equal(h->alloc-id, 0x34, msg)
639 68/push "F - test-allocate-array: sets alloc-id in handle"/imm32
641 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
643 e8/call check-ints-equal/disp32
645 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
646 # check-ints-equal(h->payload, expected-payload, msg)
648 68/push "F - test-allocate-array: sets payload in handle"/imm32
650 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
652 e8/call check-ints-equal/disp32
654 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
655 # check-ints-equal(h->payload->alloc-id, 0x34, msg)
657 68/push "F - test-allocate-array: sets alloc-id in payload"/imm32
659 ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
661 e8/call check-ints-equal/disp32
663 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
664 # check-ints-equal(h->payload->size, 3, msg)
666 68/push "F - test-allocate-array: sets array size in payload"/imm32
668 ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
670 e8/call check-ints-equal/disp32
672 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
673 # check-ints-equal(*Next-alloc-id, 0x35, msg)
675 68/push "F - test-allocate-array: increments Next-alloc-id"/imm32
677 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
679 e8/call check-ints-equal/disp32
681 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
682 # check-ints-equal(ad->curr - expected-payload, 3 + 4 for alloc-id + 4 for array size, msg)
684 68/push "F - test-allocate-array: updates allocation descriptor"/imm32
686 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
687 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
690 e8/call check-ints-equal/disp32
692 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
694 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
696 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
698 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
702 copy-array: # ad: (addr allocation-descriptor), src: (addr array _T), out: (addr handle array _T)
705 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
712 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
713 # var size/ecx: int = src->size+4
714 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
715 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx
716 # allocate(ad, size, out)
718 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
720 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
722 e8/call allocate/disp32
724 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
725 # var payload/eax: (addr byte) = out->payload
726 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
727 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
728 # . skip payload->allocid
729 05/add-to-eax 4/imm32
730 # var max/ecx: (addr byte) = payload + size
731 01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx
732 # _append-4(payload, max, src, &src->data[src->size])
733 # . . push &src->data[src->size]
734 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx
735 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 4/disp8 . # copy esi+edx+4 to edx
744 e8/call _append-4/disp32
746 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
748 # . restore registers
754 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
761 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
762 # var src/esi: (addr array int) = [3, 4, 5]
766 68/push 0xc/imm32/size
767 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
768 # var ad/ecx: allocation-descriptor containing 32 bytes
769 # . var end/ecx: (addr byte)
771 # . var start/edx: (addr byte) = end - 32
772 81 5/subop/subtract %esp 0x20/imm32
774 # . ad = {start, end}
777 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
778 # var expected-payload/ebx = ad->curr
779 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
780 # var h/edx: handle = {0, 0}
783 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
784 # *Next-alloc-id = 0x34
785 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
786 # copy-array(ad, src, h)
792 e8/call copy-array/disp32
794 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
795 # check-ints-equal(h->alloc-id, 0x34, msg)
797 68/push "F - test-copy-array: sets alloc-id in handle"/imm32
799 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
801 e8/call check-ints-equal/disp32
803 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
804 # check-ints-equal(h->payload, expected-payload, msg)
806 68/push "F - test-copy-array: sets payload in handle"/imm32
808 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
810 e8/call check-ints-equal/disp32
812 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
813 # check-ints-equal(h->payload->alloc-id, 0x34, msg)
815 68/push "F - test-copy-array: sets alloc-id in payload"/imm32
817 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
819 e8/call check-ints-equal/disp32
821 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
822 # var payload/eax: (addr int) = lookup(h)
824 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
825 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
827 e8/call lookup/disp32
829 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
830 # check-ints-equal(payload->size, 0xc, msg)
832 68/push "F - test-copy-array: sets array size in payload"/imm32
834 ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # push *eax
836 e8/call check-ints-equal/disp32
838 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
839 # check-ints-equal(*Next-alloc-id, 0x35, msg)
841 68/push "F - test-copy-array: increments Next-alloc-id"/imm32
843 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
845 e8/call check-ints-equal/disp32
847 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
848 # check-ints-equal(ad->curr - expected-payload, 12 + 4 for alloc-id + 4 for size, msg)
850 68/push "F - test-copy-array: updates allocation descriptor"/imm32
852 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
853 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
856 e8/call check-ints-equal/disp32
858 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
860 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
862 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x40/imm32 # add to esp
864 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
868 # Fill a region of memory with zeroes.
869 zero-out: # start: (addr byte), size: int
874 # if (i >= size) break
881 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
888 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
890 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
892 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx
894 # if (i >= size) break
895 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx
896 7d/jump-if->= $zero-out:end/disp8
898 c6 0/subop/copy-byte 0/mod/direct 6/rm32/esi . . . . . 0/imm8 # copy byte to *esi
903 eb/jump $zero-out:loop/disp8
905 # . restore registers
911 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
918 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
919 # region/ecx = 34, 35, 36, 37
920 68/push 0x37363534/imm32
921 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
927 e8/call zero-out/disp32
929 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
930 # first 3 bytes cleared, fourth left alone
931 # . check-ints-equal(*ecx, 0x37000000, msg)
933 68/push "F - test-zero-out"/imm32
934 68/push 0x37000000/imm32
935 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
937 e8/call check-ints-equal/disp32
939 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
941 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
943 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
947 # . . vim:nowrap:textwidth=0