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
33 # a reasonable default
38 0x100/imm32 # save a few alloc ids for fake handles
41 # instruction effective address register displacement immediate
42 # . op subop mod rm32 base index scale r32
43 # . 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
45 #? # Let's start initializing the default allocation descriptor.
49 #? # . Heap = new-segment(Heap-size)
52 #? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size
54 #? e8/call new-segment/disp32
56 #? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
58 e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
59 $array-equal-main:end:
60 # syscall_exit(Num-test-failures)
61 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
62 e8/call syscall_exit/disp32
64 # Allocate and clear 'n' bytes of memory from an allocation-descriptor 'ad'.
65 # Abort if there isn't enough memory in 'ad'.
66 allocate: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
69 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
72 # allocate-raw(ad, n, out)
74 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
75 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
76 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
78 e8/call allocate-raw/disp32
80 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
81 # eax = out->payload + 4
82 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
83 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
87 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
90 e8/call zero-out/disp32
92 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
97 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
101 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
102 # Abort if there isn't enough memory in 'ad'.
103 allocate-raw: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
106 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
115 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
117 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx
119 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx
120 # out->alloc-id = Next-alloc-id
121 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Next-alloc-id/disp32 # copy *Next-alloc-id to eax
122 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx
123 # out->payload = ad->curr
124 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
125 $allocate-raw:save-payload-in-eax:
126 89/copy 1/mod/*+disp8 2/rm32/edx . . . 0/r32/eax 4/disp8 . # copy eax to *(edx+4)
127 # *out->payload = Next-alloc-id
128 8b/copy 1/mod/*+disp8 2/rm32/edx . . . 7/r32/edi 4/disp8 . # copy *(edx+4) to edi
129 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 6/r32/esi Next-alloc-id/disp32 # copy *Next-alloc-id to esi
130 89/copy 0/mod/indirect 7/rm32/edi . . . 6/r32/esi . . # copy esi to *edi
131 $allocate-raw:increment-next-alloc-id:
132 # increment *Next-alloc-id
133 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # increment *Next-alloc-id
134 # check if there's enough space
135 # TODO: move this check up before any state updates when we support error recovery
136 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
137 3b/compare 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # compare eax with *(ecx+4)
138 73/jump-if->=-signed $allocate-raw:abort/disp8
139 $allocate-raw:commit:
141 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx
143 # . restore registers
151 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
156 # . _write(2/stderr, error)
158 68/push "allocate: failed\n"/imm32
159 68/push 2/imm32/stderr
161 e8/call _write/disp32
163 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
165 bb/copy-to-ebx 1/imm32
166 e8/call syscall_exit/disp32
169 test-allocate-raw-success:
172 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
173 # var ad/ecx: allocation-descriptor
174 68/push 0/imm32/limit
176 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
177 # ad = new-segment(512)
182 e8/call new-segment/disp32
184 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
185 # var expected-payload/ebx = ad->curr
186 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
187 # var h/edx: handle = {0, 0}
190 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
191 # *Next-alloc-id = 0x34
192 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
193 # allocate-raw(ad, 3, h)
199 e8/call allocate-raw/disp32
201 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
202 # check-ints-equal(h->alloc-id, 0x34, msg)
204 68/push "F - test-allocate-raw-success: sets alloc-id in handle"/imm32
206 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
208 e8/call check-ints-equal/disp32
210 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
211 # check-ints-equal(h->payload, expected-payload, msg)
213 68/push "F - test-allocate-raw-success: sets payload in handle"/imm32
215 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
217 e8/call check-ints-equal/disp32
219 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
220 # check-ints-equal(h->payload->alloc-id, 0x34, msg)
222 68/push "F - test-allocate-raw-success: sets alloc-id in payload"/imm32
224 ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
226 e8/call check-ints-equal/disp32
228 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
229 # check-ints-equal(*Next-alloc-id, 0x35, msg)
231 68/push "F - test-allocate-raw-success: increments Next-alloc-id"/imm32
233 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
235 e8/call check-ints-equal/disp32
237 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
238 # check-ints-equal(ad->curr - expected-payload, 3 + 4 for alloc-id, msg)
240 68/push "F - test-allocate-raw-success: updates allocation descriptor"/imm32
242 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
243 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
246 e8/call check-ints-equal/disp32
248 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
250 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
252 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
254 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
258 lookup: # h: (handle _T) -> result/eax: (addr _T)
261 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
265 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
266 # ecx = handle->alloc_id
267 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
268 # if (ecx == 0) return 0
269 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0/imm32 # compare ecx
270 74/jump-if-= $lookup:end/disp8
271 # eax = handle->address (payload)
272 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
273 # if (ecx != *eax) abort
274 39/compare 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # compare *eax and ecx
275 75/jump-if-!= $lookup:abort/disp8
277 05/add-to-eax 4/imm32
279 # . restore registers
282 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
287 # . _write(2/stderr, msg)
289 68/push "lookup failed\n"/imm32
290 68/push 2/imm32/stderr
292 e8/call _write/disp32
294 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
296 bb/copy-to-ebx 1/imm32/exit-status
297 e8/call syscall_exit/disp32
302 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
303 # var ad/ebx: allocation-descriptor
304 68/push 0/imm32/limit
306 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
307 # ad = new-segment(512)
312 e8/call new-segment/disp32
314 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
315 # var handle/ecx: handle
316 68/push 0/imm32/address
317 68/push 0/imm32/alloc-id
318 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
319 # var old-top/edx = ad->curr
320 8b/copy 0/mod/indirect 3/rm32/ebx . . . 2/r32/edx . . # copy *ebx to edx
321 # allocate-raw(ad, 2, handle)
327 e8/call allocate-raw/disp32
329 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
330 # eax = lookup(handle)
332 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
333 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
335 e8/call lookup/disp32
337 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
338 # eax contains old top of heap, except skipping the alloc-id in the payload
339 # . check-ints-equal(eax, old-top+4, msg)
341 68/push "F - test-lookup-success"/imm32
342 81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 4/imm32 # add to edx
346 e8/call check-ints-equal/disp32
348 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
350 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
352 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
354 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
358 test-lookup-null-returns-null:
361 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
362 # var handle/ecx: handle
363 68/push 0/imm32/address
364 68/push 0/imm32/alloc-id
365 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
366 # eax = lookup(handle)
368 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
369 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
371 e8/call lookup/disp32
373 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
374 # check-ints-equal(eax, 0, msg)
376 68/push "F - test-lookup-null-returns-null"/imm32
380 e8/call check-ints-equal/disp32
382 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
384 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
386 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
390 _pending-test-lookup-failure:
393 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
394 # var heap/esi: allocation-descriptor
395 68/push 0/imm32/limit
397 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
398 # heap = new-segment(512)
403 e8/call new-segment/disp32
405 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
407 68/push 0/imm32/address
408 68/push 0/imm32/alloc-id
409 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
410 # var old_top/ebx = heap->curr
411 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx
412 # first allocation, to h1
413 # . allocate(heap, 2, h1)
419 e8/call allocate/disp32
421 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
422 # reset heap->curr to mimic reclamation
423 89/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy ebx to *esi
424 # second allocation that returns the same address as the first
426 68/push 0/imm32/address
427 68/push 0/imm32/alloc-id
428 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
429 # . allocate(heap, 2, h2)
435 e8/call allocate/disp32
437 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
438 # check-ints-equal(h1->address, h2->address, msg)
440 68/push "F - test-lookup-failure"/imm32
441 ff 6/subop/push 1/mod/*+disp8 2/rm32/ecx . . . . 4/disp8 . # push *(edx+4)
442 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
444 e8/call check-ints-equal/disp32
446 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
447 # lookup(h1) should crash
449 ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4)
450 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
452 e8/call lookup/disp32
453 # should never get past this point
455 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
457 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
459 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
461 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
465 # when comparing handles, just treat them as pure values
466 handle-equal?: # a: (handle _T), b: (handle _T) -> result/eax: boolean
469 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
473 b8/copy-to-eax 0/imm32/false
474 $handle-equal?:compare-alloc-id:
476 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
477 # if (ecx != b->alloc_id) return false
478 39/compare 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # compare ecx and *(ebp+16)
479 75/jump-if-!= $handle-equal?:end/disp8
480 $handle-equal?:compare-address:
481 # ecx = handle->address
482 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
483 # if (ecx != b->address) return false
484 39/compare 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x14/disp8 . # compare ecx and *(ebp+20)
485 75/jump-if-!= $handle-equal?:end/disp8
486 $handle-equal?:return-true:
488 b8/copy-to-eax 1/imm32/true
490 # . restore registers
493 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
497 copy-handle: # src: (handle _T), dest: (addr handle _T)
500 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
505 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx
507 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
508 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx
509 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
510 89/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4)
512 # . restore registers
516 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
520 # helper: create a nested allocation descriptor (useful for tests)
521 allocate-region: # ad: (addr allocation-descriptor), n: int, out: (addr handle allocation-descriptor)
524 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
528 # allocate(ad, n, out)
530 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
531 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
532 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
534 e8/call allocate/disp32
536 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
538 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
539 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
540 # skip payload->allocid
541 05/add-to-eax 4/imm32
542 # if (eax == 0) abort
543 3d/compare-eax-and 0/imm32
544 74/jump-if-= $allocate-region:abort/disp8
545 # earmark 8 bytes at the start for a new allocation descriptor
547 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx
548 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 8/imm32 # add to ecx
549 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
550 # . *(eax+4) = eax + n
551 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx
552 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # add *(ebp+12) to ecx
553 89/copy 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy ecx to *(eax+4)
554 # . restore registers
558 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
562 # We could create a more general '$abort' jump target, but then we'd need to do
563 # a conditional jump followed by loading the error message and an unconditional
564 # jump. Or we'd need to unconditionally load the error message before a
565 # conditional jump, even if it's unused the vast majority of the time. This way
566 # we bloat a potentially cold segment in RAM so we can abort with a single
568 $allocate-region:abort:
569 # . _write(2/stderr, error)
571 68/push "allocate-region: failed to allocate\n"/imm32
572 68/push 2/imm32/stderr
574 e8/call _write/disp32
576 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
578 bb/copy-to-ebx 1/imm32
579 e8/call syscall_exit/disp32
582 # Claim the next 'n+4' bytes of memory and initialize the first 4 to n.
583 # Abort if there isn't enough memory in 'ad'.
584 allocate-array: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
587 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
593 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
594 # var size/edx: int = n+4
595 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy ecx+4 to edx
596 # allocate(ad, size, out)
598 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
600 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
602 e8/call allocate/disp32
604 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
606 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
607 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
608 # . skip payload->allocid
609 05/add-to-eax 4/imm32
611 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
613 # . restore registers
618 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
625 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
626 # var ad/ecx: allocation-descriptor
627 68/push 0/imm32/limit
629 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
630 # ad = new-segment(512)
635 e8/call new-segment/disp32
637 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
638 # var expected-payload/ebx = ad->curr
639 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
640 # var h/edx: handle = {0, 0}
643 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
644 # *Next-alloc-id = 0x34
645 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
646 # allocate-array(ad, 3, h)
652 e8/call allocate-array/disp32
654 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
655 # check-ints-equal(h->alloc-id, 0x34, msg)
657 68/push "F - test-allocate-array: sets alloc-id in handle"/imm32
659 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
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, expected-payload, msg)
666 68/push "F - test-allocate-array: sets payload in handle"/imm32
668 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+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(h->payload->alloc-id, 0x34, msg)
675 68/push "F - test-allocate-array: sets alloc-id in payload"/imm32
677 ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
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(h->payload->size, 3, msg)
684 68/push "F - test-allocate-array: sets array size in payload"/imm32
686 ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
688 e8/call check-ints-equal/disp32
690 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
691 # check-ints-equal(*Next-alloc-id, 0x35, msg)
693 68/push "F - test-allocate-array: increments Next-alloc-id"/imm32
695 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
697 e8/call check-ints-equal/disp32
699 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
700 # check-ints-equal(ad->curr - expected-payload, 3 + 4 for alloc-id + 4 for array size, msg)
702 68/push "F - test-allocate-array: updates allocation descriptor"/imm32
704 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
705 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
708 e8/call check-ints-equal/disp32
710 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
712 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
714 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
716 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
720 copy-array: # ad: (addr allocation-descriptor), src: (addr array _T), out: (addr handle array _T)
723 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
730 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
731 # var size/ecx: int = src->size+4
732 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
733 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx
734 # allocate(ad, size, out)
736 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
738 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
740 e8/call allocate/disp32
742 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
743 # var payload/eax: (addr byte) = out->payload
744 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
745 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 4/disp8 . # copy *(eax+4) to eax
746 # . skip payload->allocid
747 05/add-to-eax 4/imm32
748 # var max/ecx: (addr byte) = payload + size
749 01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx
750 # _append-4(payload, max, src, &src->data[src->size])
751 # . . push &src->data[src->size]
752 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx
753 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
762 e8/call _append-4/disp32
764 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
766 # . restore registers
772 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
779 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
780 # var src/esi: (addr array int) = [3, 4, 5]
784 68/push 0xc/imm32/size
785 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
786 # var ad/ecx: allocation-descriptor
787 68/push 0/imm32/limit
789 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
790 # ad = new-segment(512)
795 e8/call new-segment/disp32
797 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
798 # var expected-payload/ebx = ad->curr
799 8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
800 # var h/edx: handle = {0, 0}
803 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
804 # *Next-alloc-id = 0x34
805 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
806 # copy-array(ad, src, h)
812 e8/call copy-array/disp32
814 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
815 # check-ints-equal(h->alloc-id, 0x34, msg)
817 68/push "F - test-copy-array: sets alloc-id in handle"/imm32
819 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
821 e8/call check-ints-equal/disp32
823 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
824 # check-ints-equal(h->payload, expected-payload, msg)
826 68/push "F - test-copy-array: sets payload in handle"/imm32
828 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
830 e8/call check-ints-equal/disp32
832 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
833 # check-ints-equal(h->payload->alloc-id, 0x34, msg)
835 68/push "F - test-copy-array: sets alloc-id in payload"/imm32
837 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
839 e8/call check-ints-equal/disp32
841 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
842 # var payload/eax: (addr int) = lookup(h)
844 ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
845 ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
847 e8/call lookup/disp32
849 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
850 # check-ints-equal(payload->size, 0xc, msg)
852 68/push "F - test-copy-array: sets array size in payload"/imm32
854 ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # push *eax
856 e8/call check-ints-equal/disp32
858 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
859 # check-ints-equal(*Next-alloc-id, 0x35, msg)
861 68/push "F - test-copy-array: increments Next-alloc-id"/imm32
863 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # push *Next-alloc-id
865 e8/call check-ints-equal/disp32
867 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
868 # check-ints-equal(ad->curr - expected-payload, 12 + 4 for alloc-id + 4 for size, msg)
870 68/push "F - test-copy-array: updates allocation descriptor"/imm32
872 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
873 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
876 e8/call check-ints-equal/disp32
878 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
880 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
882 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
884 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
888 # Fill a region of memory with zeroes.
889 zero-out: # start: (addr byte), size: int
894 # if (i >= size) break
901 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
908 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
910 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
912 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx
914 # if (i >= size) break
915 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx
916 7d/jump-if->= $zero-out:end/disp8
918 c6 0/subop/copy-byte 0/mod/direct 6/rm32/esi . . . . . 0/imm8 # copy byte to *esi
923 eb/jump $zero-out:loop/disp8
925 # . restore registers
931 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
938 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
939 # region/ecx = 34, 35, 36, 37
940 68/push 0x37363534/imm32
941 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
947 e8/call zero-out/disp32
949 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
950 # first 3 bytes cleared, fourth left alone
951 # . check-ints-equal(*ecx, 0x37000000, msg)
953 68/push "F - test-zero-out"/imm32
954 68/push 0x37000000/imm32
955 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx
957 e8/call check-ints-equal/disp32
959 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
961 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
963 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
967 # . . vim:nowrap:textwidth=0