Initial revision
[ssr] / StraySrc / Libraries / Core / s / heap
1 ;
2 ; heap.s
3 ;
4 ; A resizing, nonshifting heap (MDW)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's heap.
12 ;
13 ; Heap is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation; either version 2, or (at your option)
16 ; any later version.
17 ;
18 ; Heap is distributed in the hope that it will be useful,
19 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ; GNU General Public License for more details.
22 ;
23 ; You should have received a copy of the GNU General Public License
24 ; along with Heap. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- New unified version --------------------------------------------------
28 ;
29 ; I'm finally fed up of maintaining two different versions of this code.
30 ; From now on, there is only this one.
31 ;
32 ; Lots of options are supported:
33 ;
34 ; OPT_APCS Generate an APCS-compatible version
35 ; OPT_SAPPHIRE Generate a Sapphire-compatible version
36 ; OPT_STANDALONE Build a standalone assembler version (default)
37 ; OPT_DLL Generate absolute address relocation for DLL code
38 ;
39 ; [mdw]
40
41 ;----- Set up some options --------------------------------------------------
42
43 MACRO
44 DCLOPT $var
45 [ :DEF:$var
46 $var SETL {TRUE}
47 |
48 GBLL $var
49 $var SETL {FALSE}
50 ]
51 MEND
52
53 DCLOPT OPT_APCS
54 DCLOPT OPT_SAPPHIRE
55 DCLOPT OPT_STANDALONE
56 DCLOPT OPT_DLL
57
58 [ :LNOT:OPT_APCS:LAND::LNOT:OPT_SAPPHIRE:LAND::LNOT:OPT_STANDALONE
59 GBLL OPT_STANDALONE
60 OPT_STANDALONE SETL {TRUE}
61 ]
62
63 ;----- Standard header ------------------------------------------------------
64
65 GET libs:header
66 GET libs:swis
67
68 ;----- External dependencies ------------------------------------------------
69
70 [ OPT_SAPPHIRE
71 GET sapphire:alloc
72 GET sapphire:flex
73 GET sapphire:msgs
74 GET sapphire:fastMove
75 GET sapphire:sapphire
76 |
77 IMPORT fastMove
78 IMPORT flex_init
79 IMPORT flex_alloc
80 IMPORT flex_extend
81 ]
82
83 ;----- Workspace macros -----------------------------------------------------
84
85 [ OPT_APCS
86
87 MACRO
88 $label WSPACE $addr,$reg
89 LCLS r
90 [ "$reg"=""
91 r SETS "R12"
92 |
93 r SETS "$reg"
94 ]
95 ALIGN
96 $label
97 LDR $r,$addr
98 [ OPT_DLL
99 LDR R14,[R10,#-536]
100 ADD $r,R14,$r
101 ]
102 MEND
103
104 ]
105
106 ;----- External routines ----------------------------------------------------
107
108 [ OPT_SAPPHIRE
109 AREA |Sapphire$$Code|,CODE,READONLY
110 ]
111 [ OPT_APCS
112 AREA |C$$Code|,CODE,READONLY
113 ]
114 [ OPT_STANDALONE
115 AREA |Straylight$$Code|,CODE,READONLY
116 ]
117
118 ; --- heap_init ---
119 ;
120 ; On entry: --
121 ;
122 ; On exit: --
123 ;
124 ; Use: Initialises the heap system for use.
125
126 EXPORT heap_init
127
128 heap_init ROUT
129
130 STMFD R13!,{R0,R1,R12,R14} ;Stash some registers
131 WSPACE heap__wSpace ;Find my workspace
132
133 [ OPT_SAPPHIRE
134 BL msgs_init ;Make sure msgs is going
135 ]
136 BL flex_init ;Make sure flex is up
137
138 [ :LNOT:OPT_STANDALONE
139
140 ; --- Ensure I'm not already initialised ---
141
142 LDR R0,heap__flex ;Get my flex pointer
143 CMP R0,#0 ;Is it a silly value?
144 LDMNEFD R13!,{R0,R1,R12,PC}^ ;Yes -- return now
145
146 ]
147
148 ; --- Read the machine chunk size ---
149
150 SWI OS_ReadMemMapInfo ;Get page size (in R0)
151 STR R0,heap__chunkSize ;Store for future reference
152
153 ; --- Allocate the base flex block ---
154
155 SUB R1,R0,#8 ;Need to allocate memory
156 ADR R0,heap__flex ;Point to flex anchor
157 BL flex_alloc ;Hope it worked
158 [ OPT_APCS
159 CMP R0,#0 ;Check the result code
160 BEQ %99heap_init ;If it failed, go ahead
161 |
162 BCS %99heap_init ;If it failed, go ahead
163 ]
164
165 ; --- Initialise the rest of the workspace ---
166
167 MOV R0,#0
168 STR R0,heap__alloced
169 MOV R0,#-1
170 STR R0,heap__freeList
171 LDR R0,heap__chunkSize
172 SUB R0,R0,#8
173 STR R0,heap__size
174 LDMFD R13!,{R0,R1,R12,PC}^
175
176 99heap_init ADR R0,heap__nomem ;Point to the error block
177 [ OPT_SAPPHIRE
178 BL msgs_error ;Translate the message
179 ]
180 SWI OS_GenerateError ;And really moan about it
181
182 heap__nomem DCD 1
183 [ OPT_SAPPHIRE
184 DCB "hpNEMI",0
185 |
186 DCB "Not enough memory to build heap",0
187 ]
188 ALIGN
189
190 [ OPT_SAPPHIRE
191 heap__wSpace DCD 0
192 ]
193
194 [ OPT_APCS
195 heap__wSpace DCD heap__sSpace
196 ]
197
198 LTORG
199
200 [ OPT_SAPPHIRE
201
202 ; --- heap_useHeap ---
203 ;
204 ; On entry: --
205 ;
206 ; On exit: --
207 ;
208 ; Use: Registers the resizing heap as the current allocator.
209
210 EXPORT heap_useHeap
211 heap_useHeap ROUT
212
213 STMFD R13!,{R0-R2,R14} ;Save some registers
214 ADR R0,heap_alloc ;Point to the allocator
215 ADR R1,heap_free ;And to the freer
216 MOV R2,#0 ;Don't care about workspace
217 BL alloc_register ;Make heap the allocator
218 LDMFD R13!,{R0-R2,PC}^ ;Return to caller
219
220 LTORG
221
222 ]
223
224 ; --- heap_info ---
225 ;
226 ; On entry: R0 == pointer to structure to fill in (APCS)
227 ;
228 ; On exit: R0 == current heap size (Sapphire)
229 ; R1 == amount of memory free in the heap (Sapphire)
230 ; R2 == size of the largest block free (Sapphire)
231 ;
232 ; Use: Describes the heap's current status.
233
234 EXPORT heap_info
235 heap_info ROUT
236
237 [ OPT_APCS
238 STMFD R13!,{R4,R5,R12,R14}
239 WSPACE heap__wSpace
240 MOV R5,R0
241 |
242 STMFD R13!,{R3,R4,R12,R14}
243 WSPACE heap__wSpace
244 ]
245
246 ; --- Set up some registers ---
247
248 LDR R0,heap__size ;Get size of heap
249
250 LDR R4,heap__flex ;Pointer to the heap
251 LDR R14,heap__freeList ;Offset to first free block
252 LDR R2,heap__alloced ;Length of allocated region
253 SUB R1,R0,R2 ;This area is free
254 MOV R2,R1 ;Largest block found yet
255
256 ; --- Now we can proceed through the loop ---
257
258 00heap_info CMP R14,#-1 ;Is this the end of the list?
259 BEQ %01heap_info ;Yes - wrap things up nicely
260 ADD R14,R14,R4 ;Convert to a pointer
261 LDR R3,[R14,#0] ;Get the length of this block
262 ADD R1,R1,R3 ;Add to free size
263 CMP R3,R2 ;Is it the biggest one yet?
264 MOVGT R2,R3 ;Yes - remember it
265 LDR R14,[R14,#4] ;Get next offset
266 B %00heap_info ;And loop round again
267
268 ; --- Now return this information ---
269
270 [ OPT_APCS
271 01heap_info STMIA R5,{R0-R2}
272 LDMFD R13!,{R4,R5,R12,PC}^
273 |
274 01heap_info LDMFD R13!,{R3,R4,R12,PC}^ ;Return to caller
275 ]
276
277 LTORG
278
279 ; --- heap_alloc ---
280 ;
281 ; On entry: R0 == size of block wanted
282 ;
283 ; On exit: Sapphire: CC if enough memory, R0 == address; CS if no
284 ; memory, R0 corrupted
285 ; APCS: R0 == pointer to memory, or zero if not enough
286 ;
287 ; Use: Allocates a block of at least a given size from a heap. If
288 ; the heap is not big enough, more is claimed from the
289 ; operating system.
290
291 EXPORT heap_alloc
292 heap_alloc ROUT
293
294 ; --- Make sure there's something to do ---
295
296 CMP R0,#0 ;Is the required size 0?
297 MOVEQS PC,R14 ;Yes -- return a NULL pointer
298
299 ; --- Start everything up then ---
300
301 [ :LNOT:OPT_APCS
302 BIC R14,R14,#C_flag
303 ]
304 STMFD R13!,{R1-R6,R12,R14} ;Save a load of registers
305 WSPACE heap__wSpace
306
307 ; --- First, set up parameters ---
308
309 ADD R0,R0,#11 ;Add overhead and align to
310 BIC R0,R0,#7 ; multiple of 8
311
312 ; --- Initialise for a loop through the free list ---
313
314 LDR R6,heap__flex ;Point to heap start
315 MOV R5,#-1 ;Previous block
316 LDR R4,heap__freeList ;Point to free list to scan
317
318 ; --- This is the free list scanning loop ---
319
320 00heap_alloc CMP R4,#-1 ;Is this the end?
321 BEQ %02heap_alloc ;Yes - try unallocated region
322 ADD R4,R4,R6 ;Translate offset to address
323 LDR R1,[R4,#0] ;Get length word
324 CMP R1,R0 ;Check sizes
325 MOVCC R5,R4 ;Too small - set up prev ptr
326 LDRCC R4,[R4,#4] ;Get next pointer
327 BCC %00heap_alloc ;And try next block
328
329 ; --- If block is right size, remove from the chain ---
330
331 LDREQ R2,[R4,#4] ;Get next offset
332 ADDEQ R2,R2,R6 ;Translate to address
333 BEQ %01heap_alloc ;And branch ahead
334
335 ; --- Now, we try to hack off the bit we need ---
336
337 STR R0,[R4,#0] ;Store block's new length
338 ADD R2,R0,R4 ;R2 points to next block
339 SUB R3,R1,R0 ;Space left in this block
340 STR R3,[R2,#0] ;Store in length field
341 LDR R3,[R4,#4] ;Now get next pointer
342 STR R3,[R2,#4] ;Store.
343
344 ; --- Now put in link from previous block and return ---
345
346 01heap_alloc SUB R2,R2,R6 ;Translate back to an offset
347 CMP R5,#-1 ;Is there a previous block?
348 STRNE R2,[R5,#4] ;Store in previous block
349 STREQ R2,heap__freeList ;No - stick it in the front
350 ADD R0,R4,#4 ;Leave out the length field
351 LDMFD R13!,{R1-R6,R12,PC}^ ;And return to caller
352
353 ; --- Check the free area is big enough for the block ---
354
355 02heap_alloc LDR R4,heap__alloced ;Offset to free area
356 LDR R5,heap__size ;Current size of heap
357 ADD R6,R4,R0 ;How big the area needs to be
358 CMP R5,R6 ;Is this sufficient?
359 BCS %03heap_alloc ;Yes - allocate block
360
361 ; --- We need more memory in the heap. Call flex for it ---
362
363 MOV R5,R0 ;Preserve the caller's length
364 LDR R1,heap__chunkSize ;Units of allocation
365 SUB R1,R1,#1 ;For alignment purposes
366 ADD R6,R6,#8
367 ADD R6,R6,R1 ;First step in alignment
368 BIC R6,R6,R1 ;Second step
369 SUB R6,R6,#8
370 ADR R0,heap__flex ;Point to anchor
371 MOV R1,R6 ;New size we want
372 BL flex_extend ;Try to get memory
373 [ OPT_APCS
374 CMP R0,#0 ;If it failed,
375 LDMEQFD R13!,{R1-R6,R12,PC}^ ;Return a null pointer
376 |
377 LDMCSFD R13!,{R1-R6,R12,R14} ;If it failed, return with...
378 ORRCSS PC,R14,#C_flag ;...carry set
379 ]
380 STR R6,heap__size ;Store new heap size
381 MOV R0,R5 ;Restore the desired length
382
383 ; --- Now allocate a block from the free area properly ---
384
385 03heap_alloc LDR R1,heap__flex ;Point to start of heap
386 ADD R2,R4,R1 ;R2 points to new block
387 STR R0,[R2,#0] ;Store the length
388 ADD R4,R4,R0 ;Get new free offset
389 STR R4,heap__alloced ;Store permanently
390 ADD R0,R2,#4 ;Point to free part of block
391 LDMFD R13!,{R1-R6,R12,PC}^ ;Return flushed with success
392
393 LTORG
394
395 ; --- heap_free ---
396 ;
397 ; On entry: R0 == pointer to a block created with heap_alloc
398 ;
399 ; On exit: --
400 ;
401 ; Use: Frees a block allocated using heap_alloc. It tries to
402 ; shrink the heap as much as possible afterwards.
403
404 EXPORT heap_free
405 heap_free ROUT
406
407 ; --- Make sure the user's not being stupid ---
408
409 CMP R0,#0
410 MOVEQS PC,R14
411
412 ; --- Proceed as normal ---
413
414 STMFD R13!,{R0-R8,R12,R14}
415 WSPACE heap__wSpace
416
417 ; --- Scan the free list ---
418 ;
419 ; Now first we run through the free list trying to find
420 ; a free block (a) immediately before the current one and
421 ; (b) immediately after the current one.
422
423 SUB R5,R0,#4 ;Point to real block start
424 LDR R6,[R5,#0] ;Get length of block
425 ADD R6,R5,R6 ;R6 points off end of block
426 MOV R7,#-1 ;Haven't found preblock
427 MOV R8,#-1 ;Haven't found postblock
428 LDR R4,heap__flex ;Pointer to start of heap
429 MOV R1,#0 ;Previous block in the chain
430 LDR R0,heap__freeList ;Scan the free list
431
432 ; --- Now do the scanning loop ---
433
434 00heap_free CMP R0,#-1 ;Have we reached the end?
435 BEQ %01heap_free ;Yes - free the block
436 ADD R0,R0,R4 ;Convert to a pointer
437 CMP R0,R6 ;Could this be postblock?
438 MOVEQ R8,R1 ;Yes - remember
439 LDR R2,[R0,#0] ;Get length
440 ADD R2,R0,R2 ;R2 points past that block
441 CMP R2,R5 ;Is this preblock?
442 MOVEQ R7,R1 ;Yes - remember
443 MOV R1,R0 ;Shift prev block along
444 LDR R0,[R0,#4] ;And get next block in list
445 B %00heap_free ;Loop round until finished
446
447 ; --- Now try and coagulate the blocks ---
448
449 01heap_free CMP R7,#-1 ;Did we find preblock?
450 BEQ %02heap_free ;No - try postblock
451 CMP R7,#0 ;Is it the first block?
452 LDREQ R0,heap__freeList ;Yes - get offset
453 LDRNE R0,[R7,#4] ;No - get offset (!)
454 ADD R0,R0,R4 ;Convert to a pointer
455 LDR R1,[R0,#0] ;Get block length
456 LDR R2,[R5,#0] ;Get length of block to free
457 ADD R1,R1,R2 ;Add them together
458 STR R1,[R0,#0] ;Store back in block
459 LDR R1,[R0,#4] ;Get next block offset
460 CMP R7,#0 ;Is there a previous block?
461 STREQ R1,heap__freeList ;No - start the list with it
462 STRNE R1,[R7,#4] ;Yes - link in as normal
463 MOV R5,R0 ;This is the block to free
464
465 CMP R8,R0 ;Is this prev to postblock?
466 MOVEQ R8,R7 ;Yes - show we've delinked
467
468 02heap_free CMP R8,#-1 ;Did we find postblock?
469 BEQ %03heap_free ;No - free the block
470 CMP R8,#0 ;Is it the first block?
471 LDREQ R0,heap__freeList ;Yes - get offset
472 LDRNE R0,[R8,#4] ;No - get offset (!)
473 ADD R0,R0,R4 ;Convert to a pointer
474 LDR R1,[R0,#0] ;Get block length
475 LDR R2,[R5,#0] ;Get length of block to free
476 ADD R1,R1,R2 ;Add them together
477 STR R1,[R5,#0] ;Store back in block
478 LDR R1,[R0,#4] ;Get next block offset
479 CMP R8,#0 ;Is there a previous block?
480 STREQ R1,heap__freeList ;No - start the list with it
481 STRNE R1,[R8,#4] ;Yes - link in as normal
482
483 ; --- Now we try to reduce the allocated area ---
484
485 03heap_free LDR R0,heap__alloced ;Find end of allocated area
486 ADD R1,R0,R4 ;Convert to a pointer
487 LDR R2,[R5,#0] ;Get length of our block
488 ADD R3,R5,R2 ;Find the end of it
489 CMP R3,R1 ;Are they the same?
490 BNE %04heap_free ;No - old-fashioned method!
491 SUB R0,R0,R2 ;Reduce the allocated region
492 STR R0,heap__alloced ;Remember this!
493 LDR R1,heap__chunkSize ;Get the chunk size
494 SUB R1,R1,#1 ;More useful like this!
495 ADD R0,R0,#8
496 ADD R0,R0,R1 ;Alignment step 1
497 BIC R7,R0,R1 ;Alignment step 2
498 SUB R7,R7,#8
499 LDR R2,heap__size ;Get current heap size
500 CMP R7,R2 ;Are they the same?
501 LDMGEFD R13!,{R0-R8,R12,PC}^ ;Yes - nothing doing
502 ADR R0,heap__flex ;Point to flex anchor
503 MOV R1,R7 ;New size for block
504 BL flex_extend ;Try and reduce
505 [ OPT_APCS
506 CMP R0,#0 ;Did it fail (unlikely!)
507 STRNE R7,heap__size ;No - remember new size
508 |
509 STRCC R7,heap__size
510 ]
511 LDMFD R13!,{R0-R8,R12,PC}^ ;Return to sender
512
513 ; --- Now use the old fashioned method to link the block ---
514
515 04heap_free LDR R0,heap__freeList ;Get current first free block
516 STR R0,[R5,#4] ;Store in our block
517 SUB R5,R5,R4 ;Turn back into an offset
518 STR R5,heap__freeList ;Store as new first free
519 LDMFD R13!,{R0-R8,R12,PC}^ ;Return to sender
520
521 LTORG
522
523 ; --- heap_reAlloc ---
524 ;
525 ; On entry: R0 == pointer to block whose size we want to change
526 ; R1 == the new size of the block
527 ;
528 ; On exit: Sapphire: CC and R0 == pointer to block if OK, CS if no mem
529 ; APCS: R0 == pointer to block if OK, or null if no mem
530 ;
531 ; Use: Changes the size of a heap block. If possible, the block's
532 ; position is unchanged, but this may not always be the case.
533 ;
534 ; Note that changing a block's size to 0 is permitted.
535
536 EXPORT heap_reAlloc
537 EXPORT heap_realloc
538 heap_reAlloc ROUT
539 heap_realloc
540
541 ; --- Some ANSI-compatible checks on the parameters ---
542
543 CMP R0,#0 ;If the pointer is NULL...
544 MOVEQ R0,R1 ;This should just do a malloc
545 BEQ heap_alloc
546
547 [ :LNOT:OPT_APCS
548 BIC R14,R14,#C_flag ;Clr carry flag on offchance
549 ]
550 STMFD R13!,{R1-R8,R12,R14}
551
552 CMP R1,#0 ;If new size is NULL
553 BLEQ heap_free ;This should just free it
554 MOVEQ R0,#0 ;Return a NULL pointer
555 LDMEQDB R13!,{R1-R8,R12,PC}^ ;And return to caller
556
557 ; --- First set things up nicely ---
558 ;
559 ; Make sure we actually have to do anything.
560
561 WSPACE heap__wSpace ;Get global heap data
562 ADD R6,R1,#11 ;Add 4 bytes length and align
563 BIC R6,R6,#7 ;to a multiple of 8
564 SUB R5,R0,#4 ;Point to length word
565 LDR R7,[R5,#0] ;Get the length word
566 CMP R7,R6 ;Do we have to do any work?
567 LDMEQDB R13!,{R1-R8,R12,PC}^ ;No - don't then!
568
569 ; --- Try for some easy cases ---
570 ;
571 ; If this block is at the end of the allocated area, we
572 ; can just extend this area and the block size. So check
573 ; for this case.
574
575 ADD R2,R5,R7 ;Find end of the block
576 LDR R8,heap__alloced ;Offset of free space
577 LDR R4,heap__flex ;Pointer to start of heap
578 ADD R3,R8,R4 ;Convert offset to pointer
579 CMP R2,R3 ;Are they the same?
580 BNE %02heap_reAlloc ;No - deal with other case
581
582 ; --- Just extend the area then ---
583 ;
584 ; Now we shall need to make sure that the free area is big
585 ; enough (or maybe reduce it!) This may, of course, fail.
586
587 SUB R8,R8,R7 ;Remove the original length
588 ADD R8,R8,R6 ;And add the new length
589 ADD R4,R8,#8 ;Add on flex's two words
590 LDR R0,heap__chunkSize ;Get the chunk size
591 SUB R0,R0,#1 ;For aligning purposes
592 ADD R4,R4,R0 ;Alignment step 1
593 BIC R4,R4,R0 ;Alignment step 2
594 SUB R4,R4,#8 ;Remove flex's words again
595 LDR R0,heap__size ;Find the heap's size
596 CMP R0,R4 ;How different are they?
597 BEQ %00heap_reAlloc ;If they're the same, no prob
598 ADR R0,heap__flex ;Point to block anchor
599 MOV R1,R4 ;Set up new size
600 BL flex_extend ;Try and change block size
601 [ OPT_APCS
602 CMP R0,#0 ;Did it fail?
603 BEQ %01heap_reAlloc ;Yes - try some other way
604 |
605 BCS %01heap_reAlloc ;Failed - try some other way
606 ]
607
608 00heap_reAlloc STR R4,heap__size ;Remember new size
609 STR R8,heap__alloced ;And new free offset
610 STR R6,[R5,#0] ;Store new length
611 ADD R0,R5,#4 ;Set up return pointer
612 B %80heap_reAlloc ;Return the pointer back
613
614 ; --- Try fiddling around inside the heap block ---
615 ;
616 ; This sets up all the registers again
617
618 01heap_reAlloc LDR R2,[R5,#0] ;Get block length
619 ADD R2,R5,R2 ;Point to end of block
620 LDR R4,heap__flex ;Point to heap base
621
622 ; --- It wasn't at the end ---
623 ;
624 ; Now, if the caller wants to reduce the block size, we can
625 ; manage that.
626
627 02heap_reAlloc SUBS R1,R7,R6 ;Is the old size bigger?
628 BLT %03heap_reAlloc ;No - we'll struggle on
629 ADD R0,R5,R6 ;Point to end of (new) block
630 STR R1,[R0,#0] ;Store in there
631 STR R6,[R5,#0] ;Store also new size in block
632 ADD R0,R0,#4 ;Point to second block
633 BL heap_free ;Attach in free chain
634 ADD R0,R5,#4 ;Setup return pointer
635 B %80heap_reAlloc ;And return to the caller
636
637 ; --- We're extending the block ---
638 ;
639 ; Loop through, finding either preblock or postblock (as for
640 ; freeing). If we can, merge these three together, and then
641 ; hope the result is big enough.
642 ;
643 ; At this point, some register usage notes might be handy:
644 ;
645 ; R2 -> end of block
646 ; R4 -> start of heap
647 ; R5 -> block to resize
648 ; R6 == new size wanted
649 ;
650 ; We will use:
651 ;
652 ; R0 -> preblock
653 ; R1 -> postblock
654 ; R3 -> list ptr
655 ;
656 ; R7 will be previous block, R8 will be used as scratch
657 ; space
658 ;
659 ; So, onwards ever...
660
661 03heap_reAlloc LDR R3,heap__freeList ;Set up list pointer
662 MOV R0,#-1 ;No preblock
663 MOV R1,#-1 ;Or postblock found yet
664 MOV R7,#0 ;Previous block invalid
665
666 ; --- Now for the loop ---
667
668 04heap_reAlloc CMP R3,#-1 ;Is this the end?
669 BEQ %05heap_reAlloc ;Try and join the blocks
670 ADD R3,R3,R4 ;Convert to an address
671 CMP R3,R2 ;Is this postblock?
672 MOVEQ R0,R7 ;Yes - remember it
673 LDR R8,[R3,#0] ;Get size of this block
674 ADD R8,R3,R8 ;And point to the end
675 CMP R5,R8 ;Is this preblock?
676 MOVEQ R1,R7 ;Yes - remember
677 MOV R7,R3 ;Move previous pointer on
678 LDR R3,[R3,#4] ;Get next one in the list
679 B %04heap_reAlloc ;Loop round for another go
680
681 ; --- Now find out if it will help to join the blocks up ---
682
683 05heap_reAlloc LDR R2,[R5,#0] ;Current size
684 CMP R0,#-1 ;Did we find preblock?
685 BEQ %06heap_reAlloc ;No - try postblock
686 CMP R0,#0 ;Is it first block in list?
687 LDREQ R3,heap__freeList ;Yes - get offset
688 LDRNE R3,[R0,#4] ;No - get offset (!)
689 LDR R7,[R3,#0] ;Get its size
690 ADD R2,R2,R7 ;Add it onto our total
691 06heap_reAlloc CMP R1,#-1 ;Did we find postblock?
692 BEQ %07heap_reAlloc ;No - check the total
693 CMP R1,#0 ;Is it first block in list?
694 LDREQ R3,heap__freeList ;Yes - get offset
695 LDRNE R3,[R1,#4] ;No - get offset (!)
696 LDR R7,[R3,#0] ;Get its size
697 ADD R2,R2,R7 ;Add it onto our total
698 07heap_reAlloc CMP R2,R6 ;Is this big enough?
699 BCC %11heap_reAlloc ;No -- do it the hard way
700
701 ; --- Now try and join on preblock ---
702 ;
703 ; Note - this is almost the same as the heap_free code, but
704 ; with different registers)
705
706 08heap_reAlloc CMP R0,#-1 ;Did we find preblock?
707 BEQ %09heap_reAlloc ;No - try postblock
708 CMP R0,#0 ;Is it first block in list?
709 LDREQ R3,heap__freeList ;Yes - get offset
710 LDRNE R3,[R0,#4] ;No - get offset (!)
711 ADD R3,R3,R4 ;Convert offset to pointer
712 LDR R7,[R3,#0] ;Get the length of the block
713 LDR R8,[R5,#0] ;Length of block to resize
714 ADD R7,R7,R8 ;Add them together
715 STR R7,[R3,#0] ;Store the combined length
716 LDR R7,[R3,#4] ;Get next block in list
717 CMP R0,#0 ;Is there a previous block?
718 STREQ R7,heap__freeList ;No - start the list with it
719 STRNE R7,[R0,#4] ;Yes - link in as normal
720
721 ; --- Now shift the data down into the bigger block ---
722
723 STMFD R13!,{R0-R3} ;Store registers
724 ADD R0,R3,#4 ;Destination data start
725 ADD R1,R5,#4 ;Source destination start
726 LDR R2,[R5,#0] ;Size of the source block
727 SUBS R2,R2,#4 ;We don't need to copy this
728 BLNE fastMove ;Does the Right Thing when
729 ;the blocks overlap
730 LDMFD R13!,{R0-R3} ;Restore our registers
731
732 ; --- Some fiddling before handling postblock ---
733
734 MOV R5,R3 ;This is now block to size
735
736 CMP R1,R3 ;Is this prev to postblock?
737 MOVEQ R1,R0 ;Yes - show we've delinked
738
739 ; --- Now process postblock in the same way ---
740
741 09heap_reAlloc CMP R1,#-1 ;Did we find postblock?
742 BEQ %10heap_reAlloc ;No - try and resize
743 CMP R1,#0 ;Is it first block in list?
744 LDREQ R3,heap__freeList ;Yes - get offset
745 LDRNE R3,[R1,#4] ;No - get offset (!)
746 ADD R3,R3,R4 ;Convert offset to pointer
747 LDR R7,[R3,#0] ;Get the length of the block
748 LDR R8,[R5,#0] ;Length of block to resize
749 ADD R7,R7,R8 ;Add them together
750 STR R7,[R5,#0] ;Store the combined length
751 LDR R7,[R3,#4] ;Get next block in list
752 CMP R0,#0 ;Is there a previous block?
753 STREQ R7,heap__freeList ;No - start the list with it
754 STRNE R7,[R0,#4] ;Yes - link in as normal
755
756 ; --- Now take off the bit we need ---
757 ;
758 ; We've already checked that it's big enough). We do the
759 ; freeing ourselves, to avoid a pointless loop in heap_free
760
761 10heap_reAlloc LDR R7,[R5,#0] ;Get the new length
762 SUB R1,R7,R6 ;Get difference in sizes
763 ADD R0,R5,R6 ;Point to end of (new) block
764 STR R1,[R0,#0] ;Store in there
765 STR R6,[R5,#0] ;Store also new size in block
766 LDR R1,heap__freeList ;Get the first block in list
767 STR R1,[R0,#4] ;Store in the new free block
768 SUB R0,R0,R4 ;Convert pointer to an offset
769 STR R0,heap__freeList ;It's now linked in
770 ADD R0,R5,#4 ;Setup return pointer
771 B %80heap_reAlloc ;Return to caller nicely
772
773 ; --- Well, we did our best ---
774 ;
775 ; All we can do now is to try and allocate a new block, copy
776 ; the data, and free the old one, causing massive
777 ; fragmentation :-(
778
779 11heap_reAlloc SUB R0,R6,#4 ;Size of block for heap_alloc
780 BL heap_alloc ;Try and allocate
781 [ OPT_APCS
782 CMP R0,#0 ;Did it fail?
783 BEQ %90heap_reAlloc ;Yes -- then return null
784 |
785 BCS %90heap_reAlloc ;Failed -- return C set
786 ]
787 MOV R7,R0 ;Remeber this pointer
788 ADD R1,R5,#4 ;Source of data to copy
789 LDR R2,[R5,#0] ;Length of stuff to copy
790 SUBS R2,R2,#4 ;Remove length word first
791 BLNE fastMove ;Move the data across
792 ADD R0,R5,#4 ;Now free the old block
793 BL heap_free ;Do the freeing business
794 MOV R0,R7 ;Give caller his pointer
795
796 ; --- Return, preserving R0 to give to the caller ---
797
798 80heap_reAlloc LDMFD R13!,{R1-R8,R12,PC}^
799
800 ; --- Return, setting the C flag ---
801
802 [ OPT_APCS
803 90heap_reAlloc MOV R0,#0
804 LDMFD R13!,{R1-R8,R12,PC}^
805 |
806 90heap_reAlloc LDMFD R13!,{R1-R8,R12,R14}
807 ORRS PC,R14,#C_flag
808 ]
809
810 LTORG
811
812 ;----- Data definitions -----------------------------------------------------
813
814 [ :LNOT:OPT_STANDALONE
815
816 ^ 0,R12
817 heap__wStart # 0
818
819 GET libs:sh.heapws
820
821 heap__wSize EQU {VAR}-heap__wStart
822
823 ]
824
825 [ OPT_SAPPHIRE
826 AREA |Sapphire$$LibData|,CODE,READONLY
827 DCD heap__wSize
828 DCD heap__wSpace
829 DCD 0
830 DCD heap_init
831 ]
832
833 [ OPT_APCS
834 AREA |C$$zidata|,DATA,NOINIT
835 heap__sSpace % heap__wSize
836 ]
837
838 ;----- That's all, folks ----------------------------------------------------
839
840 END