4 ; New heap management for Dynamite
6 ; © 1994-1998 Straylight
8 ;----- Licensing note -------------------------------------------------------
10 ; This file is part of Straylight's Dynamite
12 ; Dynamite is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation; either version 2, or (at your option)
17 ; Dynamite is distributed in the hope that it will be useful,
18 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ; GNU General Public License for more details.
22 ; You should have received a copy of the GNU General Public License
23 ; along with Dynamite. If not, write to the Free Software Foundation,
24 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 ;----- Standard header ------------------------------------------------------
34 ;----- External dependencies ------------------------------------------------
44 ;----- Macros ---------------------------------------------------------------
48 $label LDR $reg,dyn_machine ;Get the machine type
49 CMP $reg,#&A5 ;Is it a RISC PC?
52 ;----- Main code ------------------------------------------------------------
54 AREA |Dynamite$$Code|,CODE,READONLY
58 ; On entry: R0 == pointer to anchor
59 ; R1 == size to allocate in bytes
60 ; R2 == ID value to store
62 ; On exit: R0 and R1 preserved
63 ; R2 == address of block allocated
65 ; Use: Allocates a block from the Dynamite heap.
70 STMFD R13!,{R0,R1,R3,R14} ;Save some registers
71 MOV R3,R0 ;Keep anchor pointer
72 ADD R0,R1,#blk__oHead+15 ;Put size in R0...
73 BIC R0,R0,#15 ;...after mangling suitably
74 BL dh__ensure ;Ensure the memory's there
75 BVS %90dh_alloc ;If not there, return error
76 STR R1,[R0,#blk__size] ;Save the size in there
77 STR R3,[R0,#blk__anchor] ;And the anchor address
78 STR R2,[R0,#blk__id] ;Oh, and the ID value
79 ADD R2,R0,#blk__oHead ;Return correct address
80 STR R2,[R3,#0] ;And save address in anchor
81 LDMFD R13!,{R0,R1,R3,PC}^ ;And return to caller
83 90dh_alloc ADD R13,R13,#4 ;Don't restore R0 on exit
84 LDMFD R13!,{R1,R3,R14} ;Unstack some registers
85 ORRS PC,R14,#V_flag ;And return the error
91 ; On entry: R0 == pointer to anchor of block to free
95 ; Use: Frees a Dynamite block.
100 STMFD R13!,{R1,R14} ;Save some registers
101 BL dh__checkAnchor ;Make sure the anchor's OK
102 MOVVC R14,#0 ;If so, clear block's anchor
103 STRVC R14,[R1,#blk__anchor] ;Zero the anchor (R14==0 !!)
104 BLVC dh__unCompact ;Say the heap is uncompact
105 LDMFD R13!,{R1,PC} ;Return to caller
109 ; --- dh_freeWithID ---
111 ; On entry: R0 == ID of all blocks to free
115 ; Use: Frees all allocated blocks with a given ID number.
120 CMP R0,#0 ;Is he trying to free ID 0?
121 ADREQL R0,msg_errBadFreeAll ;Yes -- that's an error
122 ORREQS PC,R14,#V_flag ;So return it to him
124 ; --- Do the freeing job ---
126 ; We just tonk a 0 anchor over all blocks with a matching ID.
128 STMFD R13!,{R0-R4,R14} ;Save some registers
129 LDR R2,dyn_heapSize ;Load the current heap size
130 DIR R14 ;Is it RISC PC?
131 LDRGE R1,dyn_areaBase ;Yes -- load the area base
132 RSBLT R1,R2,#&01800000 ;No -- find the base anyway
133 ADD R2,R2,R1 ;Find the heap end address
134 MOV R3,#0 ;Haven't freed anything yet
136 00dh_freeWithID CMP R1,R2 ;Have we reached the end?
137 BGE %10dh_freeWithID ;Yes -- return then
138 LDR R14,[R1,#blk__id] ;Get the block's ID
139 SUBS R14,R14,R0 ;Is it a match? (=> R14==0)
140 STREQ R14,[R1,#blk__anchor] ;Yes -- blank out the anchor
141 MOVEQ R3,#1 ;And remember we done this
142 LDR R14,[R1,#blk__size] ;Load this block's size
143 ADD R14,R14,#blk__oHead+15 ;Add on the overhead size
144 BIC R14,R14,#15 ;And align to granularity
145 ADD R1,R1,R14 ;Move on to next block
146 B %00dh_freeWithID ;And go round again
148 ; --- We finished -- tidy up and return ---
150 10dh_freeWithID CMP R3,#0 ;Did we free anything?
151 BLNE dh__unCompact ;Yes -- then heap isn't tidy
152 LDMFD R13!,{R0-R4,PC}^ ;And return to caller
156 ; --- dh_blockInfo ---
158 ; On entry: R0 == address of block anchor
160 ; On exit: R0 preserved
161 ; R1 == address of block
162 ; R2 == size of block
165 ; Use: Returns information about a Dynamite block
170 STMFD R13!,{R14} ;Save the link register
171 BL dh__checkAnchor ;Make sure anchor is kosher
172 LDMVCIB R1,{R2,R3} ;Yes -- load size and ID
173 ADDVC R1,R1,#blk__oHead ;Point to the block
174 LDMFD R13!,{PC} ;And return to caller
178 ; --- dh_changeID ---
180 ; On entry: R0 == address of anchor block, or 0 for all
182 ; R2 == old ID (if R0 == 0)
186 ; Use: This call is use to change the ID of either an individual
187 ; block (R0 == address of anchor), or the ID of all the
188 ; blocks with the ID passed in R2 (if R0 == 0).
193 STMFD R13!,{R0-R3,R14} ;Stack some registers
194 CMP R0,#0 ;Just one block to change?
195 BEQ %50dh_changeID ;No -- jump ahead
197 ; --- Change the ID of a specific block ---
199 MOV R2,R1 ;Preserve the new ID
200 BL dh__checkAnchor ;Check the anchor
201 BVS %99dh_changeID ;It's garbish -- return error
202 STR R2,[R1,#blk__id] ;Store the new id
203 B %98dh_changeID ;And return to caller
205 ; --- Change the ID of all blocks with ID R2 ---
207 50dh_changeID LDR R3,dyn_heapSize ;Load the current heap size
208 DIR R14 ;Is it RISC PC?
209 LDRGE R0,dyn_areaBase ;Yes -- load the area base
210 RSBLT R0,R3,#&01800000 ;No -- find the base anyway
211 ADD R3,R3,R0 ;Find the heap end address
213 60dh_changeID CMP R0,R3 ;Have we reached the end?
214 BGE %98dh_changeID ;Yes -- return then
215 LDR R14,[R0,#blk__id] ;Get the id of this block
216 CMP R14,R2 ;Do we want to change it?
217 STREQ R1,[R0,#blk__id] ;Yes -- make it so then
218 LDR R14,[R0,#blk__size] ;Get the block size
219 ADD R14,R14,#blk__oHead+15 ;Add on the block size
220 BIC R14,R14,#15 ;And word align
221 ADD R0,R0,R14 ;Point to the next block
222 B %60dh_changeID ;Keep on looking for blocks
224 98dh_changeID LDMFD R13!,{R0-R3,PC}^ ;Return to caller
226 99dh_changeID ADD R13,R13,#4 ;Don't unstack R0
227 LDMFD R13!,{R1-R3,R14} ;Get back registers
228 ORRS PC,R14,#V_flag ;Return with error
232 ; --- dh__checkAnchor ---
234 ; On entry: R0 == address of anchor to check
236 ; On exit: R1 == address of block descriptor
238 ; Use: Ensures that a given anchor is valid.
242 STMFD R13!,{R14} ;Save some registers
243 LDR R1,[R0,#0] ;Load the block address
244 SUB R1,R1,#blk__oHead ;Point to our information
245 LDR R14,[R1,#blk__anchor] ;Load the block's anchor
246 CMP R0,R14 ;Do they match up?
247 ADRNEL R0,msg_errBadAnchor ;No -- point to error message
248 LDMFD R13!,{R14} ;Restore registers
249 BICEQS PC,R14,#V_flag ;Anchor OK -- clear V on exit
250 ORRNES PC,R14,#V_flag ;Anchor duff -- return error
256 ; On entry: R0 == number of bytes required (multiple of 16)
258 ; On exit: R0 == pointer to base of area allocated
260 ; Use: Ensures that there are R0 bytes available in the heap. If
261 ; there aren't R0 bytes available, it goes out of its way to
262 ; ensure that there *are* by getting more. If there still
263 ; isn't enough, it compacts the heap and tries some more.
267 STMFD R13!,{R1-R5,R14} ;Save some registers
268 MOV R1,R0 ;Keep the size I want
270 ; --- Try to find some space among the free blocks ---
272 LDR R3,dyn_heapSize ;Load the current heap size
273 DIR R14 ;Is it RISC PC?
274 LDRGE R2,dyn_areaBase ;Yes -- load the area base
275 RSBLT R2,R3,#&01800000 ;No -- find the base anyway
276 ADD R3,R3,R2 ;Find the heap end address
277 MOV R5,#0 ;No bytes found yet
279 00dh__ensure CMP R2,R3 ;Is there more to go?
280 BGE %05dh__ensure ;No -- then extend the heap
282 LDR R0,[R2,#blk__size] ;Load this block's size
283 ADD R0,R0,#blk__oHead+15 ;Add on the overhead size
284 BIC R0,R0,#15 ;And align to granularity
285 LDR R14,[R2,#blk__anchor] ;Is the block free?
286 ADD R2,R2,R0 ;Bump on block pointer
287 CMP R14,#0 ;If so, anchor==0
288 MOVNE R5,#0 ;If not, clear free size
289 BNE %00dh__ensure ;And loop round again
291 ; --- Found a free block ---
293 CMP R5,#0 ;Is there a free block size?
294 SUBEQ R4,R2,R0 ;No -- this is the start then
295 ADD R5,R5,R0 ;Add on the size of this blk
296 SUBS R14,R5,R1 ;Is this big enough?
297 BLT %00dh__ensure ;No -- keep on round
299 ; --- Found a big enough space ---
301 BEQ %03dh__ensure ;If no leftover, skip on
302 ADD R3,R4,R1 ;Point to bit left over
303 MOV R0,#0 ;This block is free
304 SUB R2,R14,#blk__oHead ;Subtract info overhead
305 STMIA R3,{R0,R2} ;Save this in the block
307 ; --- Return address of this memory ---
309 03dh__ensure MOV R0,R4 ;Point to the free block
310 LDMFD R13!,{R1-R5,R14} ;Unstack registers
311 BICS PC,R14,#V_flag ;And clear error indicator
313 ; --- Main size ensuring loop ---
315 05dh__ensure LDR R14,dyn_areaSize ;Get the dynamic area size
316 LDR R2,dyn_heapSize ;And the size we're using
317 SUB R3,R14,R2 ;How much do we have?
318 SUBS R3,R1,R3 ;And is it enough?
319 BLE %10dh__ensure ;Yes -- skip onwards
321 ; --- Try to get some more pages ---
323 LDR R14,dyn_pageSize ;Load machine page size
324 SUB R14,R14,#1 ;Subtract one -- round up
325 ADD R3,R3,R14 ;Add it on for rounding
326 LDR R4,dyn_log2PageSize ;Load the page size log
327 MOV R0,R3,LSR R4 ;How many do I need?
328 BL da_addPages ;Get some more
329 BVC %10dh__ensure ;It worked -- skip onwards
331 ; --- Hmm... -- try compacting the heap ---
333 BL dh_compact ;Try to compact the heap
334 BCC %05dh__ensure ;If it did, try again
336 ADRL R0,msg_errNoMem ;Point to the error
337 LDMFD R13!,{R1-R5,R14} ;Restore the registers
338 ORRS PC,R14,#V_flag ;And return to caller
340 ; --- Extend the heap and return the base address ---
342 10dh__ensure ADD R14,R2,R1 ;Work out the new heap size
343 STR R14,dyn_heapSize ;Save this away for later
344 DIR R0 ;Get the heap's direcection
345 LDRGE R0,dyn_areaBase ;Yes -- load the area base
346 ADDGE R0,R0,R2 ;And add the old heap size
347 RSBLT R0,R14,#&01800000 ;Otherwise find the heap base
348 LDMFD R13!,{R1-R5,R14} ;Unstack registers
349 BICS PC,R14,#V_flag ;And clear error indicator
357 ; On exit: CS if there was nothing we could do
359 ; Use: Tries to shunt the free space in the heap off the end and
360 ; back into the operating system's free pool. It does it a
361 ; little bit and then stops, rather like those workmen on the
367 STMFD R13!,{R14} ;Save some registers
368 LDR R14,dyn_hpFlags ;Load the heap's flags
369 TST R14,#hpFlag_tidy ;Is the heap tidy?
370 LDREQ R14,dyn_lockCount ;No -- then load lock count
371 CMPEQ R14,#0 ;Is the heap locked?
372 BNE %91dh_reduce ;Yes -- then return CS now
374 ; --- Do search for free blocks ---
376 STMFD R13!,{R0-R9} ;Save some more registers
377 LDR R1,dyn_heapSize ;Load the current heap size
378 DIR R14 ;Is it RISC PC?
379 LDRGE R5,dyn_areaBase ;Yes -- load the area base
380 RSBLT R5,R1,#&01800000 ;No -- find the base anyway
381 ADD R1,R1,R5 ;Find the heap end address
382 MOV R9,R5 ;Remember heap base address
384 ; --- Find a free block ---
386 MOV R7,#0 ;No previous block
387 00dh_reduce CMP R5,R1 ;Are we at the end yet?
388 BGE %04dh_reduce ;Yes -- jump ahead a little
390 LDR R2,[R5,#blk__anchor] ;Get the block's anchor addr
391 CMP R2,#0 ;Is the block free?
392 LDR R2,[R5,#blk__size] ;Get the block size
393 ADD R2,R2,#blk__oHead+15 ;Add on the overhead bytes
394 BIC R2,R2,#15 ;And word align the size
395 MOVNE R7,R5 ;No -- remember where it is
396 ADDNE R5,R5,R2 ;...move on to next one
397 BNE %00dh_reduce ;...go round for another one
399 ; --- We've found a free block ---
401 01dh_reduce ADD R6,R5,R2 ;Point to the next block
402 CMP R6,R1 ;Is that the end of the heap?
403 MOVGE R8,R7 ;Yes -- set up prev pointer
404 SUBGE R2,R2,#blk__oHead ;...take off overhead
405 STRGE R2,[R5,#blk__size] ;...store overall block size
406 BGE %04dh_reduce ;...and jump ahead a little
408 ; --- Check for two free blocks together ---
410 LDR R0,[R6,#blk__anchor] ;Does this have an anchor?
411 CMP R0,#0 ;Check if it's free
412 SUBNE R2,R2,#blk__oHead ;Not -- take off overhead
413 STRNE R2,[R5,#blk__size] ;...store overall block size
414 BNE %02dh_reduce ;...jump ahead a little
416 ; --- Join two adjacent free blocks together ---
418 LDR R0,[R6,#blk__size] ;Yes -- get its size
419 ADD R2,R0,R2 ;Concatenate the two blocks
420 ADD R2,R2,#blk__oHead+15 ;Add on the overhead bytes
421 BIC R2,R2,#15 ;And word align the size
422 B %01dh_reduce ;And check again...
424 ; --- We may be searching for the last block ---
427 DIR R14 ;Get the heap direction
428 MOVLT R5,R6 ;Down -- point to next block
429 MOVLT R8,R7 ;...remember last block pos.
430 BLT %00dh_reduce ;...and keep on searching
432 ; --- There's a block to bring down ---
434 LDR R4,[R6,#blk__size] ;Get size of block to move
435 ADD R4,R4,#blk__oHead+15 ;Add the flex overhead
436 BIC R4,R4,#15 ;And word align the size
437 MOVS R2,R4 ;This is the size to move
438 MOV R0,R5 ;Where to move it to
439 MOV R1,R6 ;Where it is right now
440 BLNE dh__move ;Copy it down PDQ
441 ADD R0,R5,R4 ;Point after block we moved
442 MOV R1,#0 ;Block doesn't have an anchor
443 STR R1,[R0,#blk__anchor] ;Store that away for later
444 SUB R1,R6,R5 ;Find the difference here
445 SUB R1,R1,#blk__oHead ;Don't count this size here
446 STR R1,[R0,#blk__size] ;Store the old size in free
448 ; --- We need to fix up the block we moved ---
450 LDR R0,[R5,#blk__anchor] ;Get the anchor pointer
451 ADD R1,R5,#blk__oHead ;Point to the real data
452 STR R1,[R0] ;Store client's new anchor
454 ; --- That's it -- return to caller ---
456 B %10dh_reduce ;Return to caller
458 ; --- We've reached the end of the heap ---
460 ; Now things get a little more complicated:
462 ; If the heap goes upwards, then there may be a free block
463 ; on the end that we can free (R5 < R1), otherwise the heap
466 ; If the heap goes downwards, then R8 points to the block
467 ; immediately before the last free one. If R8 is 0, then
468 ; either the heap is compacted (the first block is not free)
469 ; or there is only on free block, and it's at the lower
473 DIR R14 ;Get the heap direction
474 BGE %05dh_reduce ;Upwards -- jump ahead
476 CMP R8,#0 ;Was there a previous block?
477 BNE %07dh_reduce ;Yes -- compact heap then
479 LDR R14,[R9,#blk__anchor] ;Get previous block anchor
480 CMP R14,#0 ;Is the first block free?
481 BNE %90dh_reduce ;No -- the heap is compacted
483 LDR R14,[R9,#blk__size] ;Get the size of the free blk
484 ADD R14,R14,#blk__oHead+15 ;Add on the overhead
485 BIC R14,R14,#15 ;And correctly align
486 ADD R7,R9,R14 ;Point past the free block
488 LDR R0,dyn_areaSize ;Get the area size
489 RSB R14,R0,#&01800000 ;Find the base address
490 SUB R5,R7,R14 ;Get size of unsused area
491 SUB R14,R0,R5 ;Get the area size left
492 STR R14,dyn_heapSize ;Store it away nicely
493 LDR R14,dyn_log2PageSize ;Get the log page size
494 MOVS R0,R5,LSR R14 ;How many pages can we free?
495 BLNE da_removePages ;More than 0 -- free them
496 B %20dh_reduce ;Return to caller
498 ; --- Merge the last block with the free area ---
500 05dh_reduce CMP R5,R1 ;Had we reached the end?
501 BGE %90dh_reduce ;Yes -- heaps compact then
503 SUB R14,R5,R9 ;Get the used area size
504 STR R14,dyn_heapSize ;And store it away
505 LDR R0,dyn_pageSize ;Get the machine page size
506 SUB R0,R0,#1 ;Turn into a bitmask
507 ADD R5,R5,R0 ;Align this to page boundary
508 BIC R0,R5,R0 ;And finish off the align
509 LDR R1,dyn_areaSize ;Get the dyn area size
510 ADD R1,R1,R9 ;Point to end of area
511 SUBS R0,R1,R0 ;Are these different?
512 BEQ %10dh_reduce ;No -- return
513 LDR R14,dyn_log2PageSize ;Get the log page size
514 MOVS R0,R0,LSR R14 ;How many pages can we free?
515 BLNE da_removePages ;More than 0 -- free them
516 B %20dh_reduce ;Return to caller
518 ; --- Move a block in a downwards heap ---
520 07dh_reduce LDR R2,[R8,#blk__size] ;Get size of block to move
521 ADD R2,R2,#blk__oHead+15 ;Add the flex overhead
522 BIC R2,R2,#15 ;And word align the size
523 ADD R0,R8,R2 ;Point to the free block
524 LDR R4,[R0,#blk__size] ;Load out it's size
525 ADD R14,R4,#blk__oHead+15 ;Add the flex overhead
526 BIC R14,R14,#15 ;And word align the size
527 ADD R0,R0,R14 ;Point to the end of the blk
528 SUB R0,R0,R2 ;Copy the block to here
529 MOV R1,R8 ;Where it is right now
530 BLNE dh__move ;Copy it down PDQ
531 MOV R1,#0 ;Block doesn't have an anchor
532 STR R1,[R8,#blk__anchor] ;Store that away for later
533 STR R4,[R8,#blk__size] ;Store the old size in free
535 ; --- We need to fix up the block we moved ---
537 LDR R14,[R0,#blk__anchor] ;Get the anchor pointer
538 ADD R0,R0,#blk__oHead ;Point to the real data
539 STR R0,[R14] ;Store client's new anchor
541 ; --- That's it -- return ---
543 10dh_reduce LDMFD R13!,{R0-R9,R14} ;Load back registers
544 BICS PC,R14,#C_flag ;Return with C clear
546 ; --- There wasn't anything to do -- we're compacted ---
548 20dh_reduce LDR R0,dyn_hpFlags ;Load my flags
549 ORR R0,R0,#hpFlag_tidy ;We're compacted
550 STR R0,dyn_hpFlags ;Store back the flags
551 LDMFD R13!,{R0-R9,R14} ;Load back registers
552 BICS PC,R14,#C_flag ;Return with C clear
554 ; --- Nothing could be done ---
556 90dh_reduce LDMFD R13!,{R0-R9} ;Load back registers
557 91dh_reduce LDMFD R13!,{R14} ;And the link too
558 ORRS PC,R14,#C_flag ;Return with C set
568 ; Use: Does a full compaction of the heap.
573 STMFD R13!,{R14} ;Stack the link register
574 BL dh_reduce ;Try to reduce the heap
575 LDMCSFD R13!,{PC} ;If it couldn't, return CS
576 00dh_compact BL dh_reduce ;Try to reduce the heap
577 BCC %00dh_compact ;Did something -- try again
578 LDMFD R13!,{R14} ;Return to caller...
579 BICS PC,R14,#C_flag ;... saying we did something
583 ; --- dh__unCompact ---
589 ; Use: Marks the heap as being uncompact.
593 STMFD R13!,{R14} ;Stack the link register
594 LDR R14,dyn_hpFlags ;Load the flags word
595 TST R14,#hpFlag_tidy ;Is the tidy bit on?
596 BICNE R14,R14,#hpFlag_tidy ;Clear the 'is tidy' bit
597 STRNE R14,dyn_hpFlags ;Store back the flags
598 BLNE dt_message ;Yes -- prod compactor
599 LDMFD R13!,{PC}^ ;Return to caller
607 ; On exit: R10 corrupted (SWIs don't care about this)
609 ; Use: Locks the heap, so that compaction entirely fails to happen.
614 LDR R10,dyn_lockCount ;Load the old lock count
615 ADD R10,R10,#1 ;Bump the counter a little
616 STR R10,dyn_lockCount ;Store the counter back
617 MOVS PC,R14 ;And return to caller
625 ; On exit: R10 corrupted (SWIs don't care about this)
627 ; Use: Unlocks the heap, so that compaction can happen again, maybe.
632 LDR R10,dyn_lockCount ;Load the old lock count
633 SUBS R10,R10,#1 ;Knock the counter down
634 STRGE R10,dyn_lockCount ;Store the counter back
635 BEQ dt_message ;If now enabled, signal task
636 MOVS PC,R14 ;And return to caller
642 ; On entry: R0 == mask of registers to save
644 ; On exit: R10, R11 corrupted
646 ; Use: Saves a load of registers on the Dynamite relocation stack.
647 ; The mask in R0 contains a bit set for each register to save:
648 ; bit 3 set means save R3 etc. Since this is a SWI, only
649 ; R1-R9 can be saved on the stack.
654 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
656 LDR R10,dyn_stackPtr ;Load the stack pointer
674 05 MOVS R11,R0,LSL #25
681 STR R10,dyn_stackPtr ;Save stack pointer back
682 MOVS PC,R14 ;And return to caller
688 ; On entry: R0 == mask of registers to load
690 ; On exit: R10, R11 corrupted
692 ; Use: Loads a load of registers on the Dynamite relocation stack.
693 ; The mask in R0 contains a bit set for each register to load:
694 ; bit 3 set means load R3 etc. Since this is a SWI, only
695 ; R1-R9 can be read from the stack.
700 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
702 LDR R10,dyn_stackPtr ;Load the stack pointer
721 05 MOVS R11,R0,LSL #29
727 STR R10,dyn_stackPtr ;Save stack pointer back
728 MOVS PC,R14 ;And return to caller
734 ; On entry: R0 == address of block anchor
735 ; R1 == new size for block
737 ; On exit: R0 preserved
738 ; R1 == address of block, may have moved
740 ; Use: Changes the size of a block.
745 STMFD R13!,{R2,R14} ;Save some registers
746 MOV R2,R1 ;Keep the size safe a while
747 BL dh__checkAnchor ;Make sure anchor's kosher
748 LDRVC R1,[R1,#blk__size] ;Load the size word nicely
749 SUBVC R2,R2,R1 ;Get the `by' value I need
750 BLVC dh_midExtend ;Do the messing about
751 LDMFD R13!,{R2,PC} ;Return to caller
755 ; --- dh_midExtend ---
757 ; On entry: R0 == address of block anchor
758 ; R1 == byte offset from block start
759 ; R2 == number of bytes to insert
761 ; On exit: R0 preserved
762 ; R1 == address of block, may have moved
764 ; Use: Inserts or removes bytes at a given offset into a Dynamite
770 STMFD R13!,{R0,R2-R9,R14} ;Save some registers
771 MOV R5,R0 ;Keep the anchor address
772 MOV R6,R1 ;And the byte offset
773 MOV R7,R2 ;And the size to insert
775 ; --- To start with, some sanity checks ---
777 BL dh__checkAnchor ;Make sure anchor is OK
778 BVS %90dh_midExtend ;If not, return error
779 CMP R7,#0 ;Are we growing the block?
780 ADDLT R14,R6,R7 ;No -- get lowest byte access
781 MOVGE R14,R6 ;Yes -- similarly
782 CMP R14,#0 ;Is this off the end?
783 ADRLTL R0,msg_errBadMid ;Yes -- point to error
784 BLT %90dh_midExtend ;And return to caller
785 LDR R4,[R1,#blk__size] ;Get the block's size
786 CMP R6,R4 ;Are we going too far here?
787 ADRGTL R0,msg_errBadMid ;Yes -- point to error
788 BGT %90dh_midExtend ;And return to caller
790 ; --- Now do the correct extend job ---
792 CMP R7,#0 ;Are we growing the block?
793 BEQ %85dh_midExtend ;Not changing -- return
794 BLT %50dh_midExtend ;Shrinking -- skip to do that
796 ; --- Make a block bigger ---
798 ; We do this in 3 stages:
800 ; * Get the amount of dead space at the end of the block, and
801 ; see if this is enough.
803 ; * If not, gather together the free blocks immediately
804 ; following the block and add this to the dead space.
806 ; * If we still don't have enough, we ensure a block of the
807 ; required size (plus block descriptor) and copy the data
810 ; Registers will be used as follows:
812 ; R5-R7 are original arguments
813 ; R4 == current size of block
814 ; R3 == size of area we have found
815 ; R2 == base address of extension area
816 ; R1 == base address of block
818 ADD R2,R1,R4 ;Find the end of the block
819 ADD R3,R4,#blk__oHead+15 ;Align block size to gran.
820 BIC R0,R3,#15 ;To get dead space too
821 SUB R3,R0,#blk__oHead ;But don't have the overhead
822 SUB R3,R3,R4 ;Get size of the dead space
823 CMP R3,R7 ;Is there enough for us?
824 BGE %30dh_midExtend ;Yes -- use it then
826 ; --- Now we must gather free blocks together nicely ---
828 STMFD R13!,{R8-R10} ;Save some more registers
829 ADD R8,R1,R0 ;Find start of next block
830 DIR R14 ;Which way is the heap going?
831 LDRGE R14,dyn_areaBase ;Upwards -- get base address
832 LDRGE R10,dyn_heapSize ;And the heap's size
833 ADDGE R10,R14,R10 ;To get the top of the heap
834 MOVLT R10,#&01800000 ;Downwards -- get heap top
836 05dh_midExtend CMP R8,R10 ;Are we there yet?
837 BGE %10dh_midExtend ;Yes -- stop there then
838 LDR R14,[R8,#blk__anchor] ;Get the block's anchor
839 CMP R14,#0 ;Is it a free block?
840 BNE %10dh_midExtend ;No -- stop here then
841 LDR R9,[R8,#blk__size] ;Get the block's size
842 ADD R9,R9,#blk__oHead+15 ;Add information overhead
843 BIC R9,R9,#15 ;And align size nicely
844 ADD R8,R8,R9 ;Move on to next block
845 ADD R3,R3,R9 ;And increase available space
846 CMP R3,R7 ;Do we have enough yet?
847 BLT %05dh_midExtend ;No -- go round again
849 LDMFD R13!,{R8-R10} ;Restore the stack pointer
850 B %30dh_midExtend ;And do the extend op
852 ; --- Not enough space in free blocks ---
854 ; We dh__ensure enough space in the heap, and copy the whole
857 10dh_midExtend LDMFD R13!,{R8-R10} ;Restore the stack pointer
858 ADD R0,R4,R7 ;Get the required size
859 ADD R0,R0,#blk__oHead+15 ;Add information overhead
860 BIC R0,R0,#15 ;And align size nicely
861 BL dh__ensure ;Make the space available
862 BVS %90dh_midExtend ;If it failed, return error
863 LDR R1,[R5,#0] ;Reload anchor -- may move
864 SUB R1,R1,#blk__oHead ;And find real block base
865 ADD R2,R4,#blk__oHead ;Add on the info overhead
866 BL dh__move ;Copy the data over
867 ADD R14,R0,#blk__oHead ;Point to the usable area
868 STR R14,[R5,#0] ;Save client's new anchor
869 MOV R14,#0 ;Get a zero word
870 STR R14,[R1,#blk__anchor] ;To mark old block as free
871 BL dh__unCompact ;The heap is not compact now
873 ; --- Set up registers for the resize op ---
875 MOV R1,R0 ;Point at the new block base
876 ADD R2,R1,R4 ;Find area to extend from
877 MOV R3,R7 ;And the size we have found
879 ; --- Perform the block resize ---
881 30dh_midExtend ADD R14,R4,R7 ;Get the new block size
882 STR R14,[R1,#blk__size] ;And save it away
883 ADD R3,R3,R4 ;Get the total area size
884 ADD R14,R14,#blk__oHead+15 ;Add overhead to new size
885 BIC R14,R14,#15 ;And align nicely
887 ; --- Increase the heap size if we need to ---
889 ADD R0,R1,R14 ;Find the end of the area
890 LDR R9,dyn_heapSize ;Load the current heap size
891 DIR R8 ;Is it RISC PC?
892 LDRGE R8,dyn_areaBase ;Yes -- load the area base
893 RSBLT R8,R9,#&01800000 ;No -- find the base anyway
894 ADD R9,R9,R8 ;Find the heap end address
895 CMP R0,R9 ;Is end too high?
896 SUBGT R0,R0,R8 ;Yes -- get the heap size
897 STRGT R0,dyn_heapSize ;...and store it back again
899 ADD R3,R3,#blk__oHead+15 ;Do the same for the whole...
900 BIC R3,R3,#15 ;... area size
901 SUBS R3,R3,R14 ;Get the space left at end
902 BLE %35dh_midExtend ;Perfect fit -- skip on
904 ; --- Insert a free block here ---
906 ADD R0,R1,R14 ;Find the end of the area
907 MOV R2,#0 ;No anchor -- it's free
908 SUB R3,R3,#blk__oHead ;Subtract overhead size
909 STMIA R0,{R2,R3} ;Save in descriptor block
911 ; --- Now split the block as required ---
913 35dh_midExtend ADD R1,R1,#blk__oHead ;Point at usable part of blk
914 ADD R1,R1,R6 ;Find the split offset
915 ADD R0,R1,R7 ;Find where to move to
916 SUBS R2,R4,R6 ;How much we have to move
917 BLNE dh__move ;Do the split op
918 B %85dh_midExtend ;And return happily to caller
920 ; --- We have to reduce a block ---
922 50dh_midExtend ADD R14,R4,R7 ;Get the new size
923 STR R14,[R1,#blk__size] ;This is the new size
924 ADD R1,R1,#blk__oHead ;Point at usable part of blk
925 ADD R1,R1,R6 ;Find the split offset
926 ADD R0,R1,R7 ;Find where to move to
927 SUBS R2,R4,R6 ;How much we have to move
928 BLNE dh__move ;Do the split op
930 ; --- Now update the size and insert free block ---
932 ADD R3,R4,R7 ;Find the new size
933 ADD R4,R4,#blk__oHead+15 ;Add overhead to new size
934 BIC R4,R4,#15 ;And align nicely
935 ADD R3,R3,#blk__oHead+15 ;Do the same for the whole...
936 BIC R3,R3,#15 ;... area size
937 SUBS R14,R4,R3 ;Get the space left at end
938 BEQ %85dh_midExtend ;Perfect fit -- skip onwards
940 ; --- Insert a free block here ---
942 LDR R1,[R5,#0] ;Load the block address
943 SUB R1,R1,#blk__oHead ;Point to the block descr.
944 ADD R0,R1,R3 ;Find the end of the area
945 MOV R2,#0 ;No anchor -- it's free
946 SUB R3,R14,#blk__oHead ;Subtract overhead size
947 STMIA R0,{R2,R3} ;Save in descriptor block
948 BL dh__unCompact ;The heap is not compact now
950 ; --- Now everything is great ---
952 85dh_midExtend LDR R1,[R5,#0] ;Load the block address
953 LDMFD R13!,{R0,R2-R9,R14} ;Unstack registers
954 BICS PC,R14,#V_flag ;And return to caller
956 ; --- We failed miserably ---
958 90dh_midExtend ADD R13,R13,#4 ;Don't restore R0 on exit
959 LDMFD R13!,{R2-R9,R14} ;Unstack registers
960 ORRS PC,R14,#V_flag ;And return to caller
966 ; On entry: R0 == destination of movement
967 ; R1 == base of block to move
968 ; R2 == size of block to move
972 ; Use: Shunts memory around in the heap, relocating everything that
977 STMFD R13!,{R3,R4,R14} ;Save some registers
978 BL fastMove ;Do the memory movement
980 ; --- Now relocate entries on the stack ---
982 10dh__move LDR R4,dyn_stackPtr ;Find the stack pointer now
983 ADR R3,dyn_stack ;Point to the stack base
984 15dh__move CMP R3,R4 ;Have we finished yet?
985 BGE %20dh__move ;Yes -- return then
986 LDR R14,[R3],#4 ;Load next entry from stack
987 SUB R14,R14,R1 ;Subtract source address
988 CMP R14,R2 ;Is it in the block?
989 ADDLO R14,R14,R0 ;Yes -- relocate
990 STRLO R14,[R3,#-4] ;And store back in stack
991 B %15dh__move ;And carry on relocating
993 20dh__move LDMFD R13!,{R3,R4,PC}^ ;Return to caller
997 ; --- dh_checkHeap ---
1001 ; On exit: May return an error
1003 ; Use: Checks the current internal format of the heap to make
1004 ; sure that it hasn't been corrupted in any way.
1005 ; If the integrity check fails then an error is returned.
1010 STMFD R13!,{R0-R7,R14} ;Save some registers
1012 ; --- Start going throught the blocks ---
1014 LDR R6,dyn_heapSize ;Load the current heap size
1015 DIR R14 ;Is it RISC PC?
1016 LDRGE R5,dyn_areaBase ;Yes -- load the area base
1017 RSBLT R5,R6,#&01800000 ;No -- find the base anyway
1018 ADD R6,R6,R5 ;Find the heap end address
1019 MOV R9,R5 ;Remember heap base address
1021 ; --- Find a block ---
1023 MOV R7,R5 ;Previous block
1024 00dh_checkHeap CMP R5,R6 ;Are we at the end yet?
1025 ADRGTL R0,msg_errBadHeap2 ;Oops -- must have a bad len
1026 BGT %90dh_checkHeap ;Gone past -- oops
1027 BEQ %50dh_checkHeap ;Yes -- jump ahead a little
1029 MOV R0,R5 ;Get the base of area to chk
1030 ADD R1,R0,#blk__oHead ;Get the overhead size
1031 SWI OS_ValidateAddress ;Make sure this is kosher
1032 ADRCSL R0,msg_errBadHeap2 ;No -- must have a bad len
1033 BCS %90dh_checkHeap ;If not, moan at client
1034 LDR R2,[R5,#blk__anchor] ;Get the block's anchor addr
1035 CMP R2,#0 ;Is the block free?
1036 BEQ %10dh_checkHeap ;Yes -- jump ahead
1038 ; --- Make sure the anchor checks OK ---
1040 MOV R7,R5 ;This block could be wrong
1041 MOV R0,R2 ;Get the base of area to chk
1042 ADD R1,R0,#4 ;Just check one word
1043 SWI OS_ValidateAddress ;Make sure this is kosher
1044 ADRCSL R0,msg_errBadHeap3 ;Address must be dead then
1045 BCS %90dh_checkHeap ;If not, moan at client
1047 LDR R0,[R2,#0] ;Load the anchors value
1048 ADD R14,R5,#blk__oHead ;This is what R0 should be
1049 CMP R0,R14 ;Do they match?
1050 ADRNEL R0,msg_errBadHeap1 ;No -- point to the error
1051 BNE %90dh_checkHeap ;And return it joyfully
1053 ; --- Go round for more then ---
1055 10dh_checkHeap LDR R3,[R5,#blk__size] ;Get the block size
1056 ADD R3,R3,#blk__oHead+15 ;Add on the overhead bytes
1057 BIC R3,R3,#15 ;And word align the size
1058 ADD R5,R5,R3 ;Yes -- move on to next one
1059 B %00dh_checkHeap ;...go round for another one
1061 50dh_checkHeap LDMFD R13!,{R0-R7,R14} ;Load registers back
1062 BICS PC,R14,#V_flag ;Return without error
1064 90dh_checkHeap ADD R13,R13,#4
1065 STR R7,[R0,#0] ;Store as the error number!
1066 LDMFD R13!,{R1-R7,R14} ;Load registers back
1067 ORRS PC,R14,#V_flag ;Return with error
1071 ; --- dh_changeAnchor ---
1073 ; On entry: R0 == pointer to anchor for block
1074 ; R1 == address of new anchor
1078 ; Use: Adjusts a block's anchor, in case it moves.
1080 EXPORT dh_changeAnchor
1081 dh_changeAnchor ROUT
1083 STMFD R13!,{R2,R14} ;Save a register
1084 MOV R2,R1 ;Remember this value
1085 BL dh__checkAnchor ;Make sure the anchor's OK
1086 STRVC R2,[R1,#blk__anchor] ;Save the new anchor pointer
1087 ADDVC R14,R1,#blk__oHead ;Skip onto the actual data
1088 STRVC R14,[R2,#0] ;And set the new anchor up
1089 LDMFD R13!,{R2,PC} ;Return to caller
1099 ; Use: Outputs a textual description of the dynamite heap, giving
1100 ; details of each block within it.
1102 dh__preDump LDR R12,[R12]
1107 STMFD R13!,{R0-R4,R14} ;Stack some registers
1109 LDR R4,dyn_heapSize ;Get the current heap size
1110 DIR R14 ;Which direction does it go?
1111 LDRGE R2,dyn_areaBase ;Up -- find the base then
1112 RSBLT R2,R4,#&1800000 ;Down -- start below the RMA
1113 ADD R3,R2,R4 ;Find the heap limit address
1115 ; --- Display information about the heap in general ---
1117 ADRL R0,msg_dumpHpBase ;Find the message
1118 MOV R1,R2 ;Get the heap base address
1119 BL dh__writeHex ;And display it
1121 MOV R1,R4 ;Get the heap size
1122 ADRL R0,msg_dumpHpSize ;Find the message
1123 BL dh__writeHex ;And display it
1125 LDR R1,dyn_areaSize ;Get the dynamic area size
1126 ADRL R0,msg_dumpHpArSz ;Find the message
1127 BL dh__writeHex ;And display it
1129 ; --- Now start on the loop ---
1131 00 CMP R2,R3 ;Have we finished yet?
1132 LDMCSFD R13!,{R0-R4,PC}^ ;Yes -- then return
1134 SWI OS_NewLine ;Start a new line here
1136 ADRL R0,msg_dumpBlkAddr ;Point to the message
1137 ADD R1,R2,#blk__oHead ;Point to the current block
1138 BL dh__writeHex ;Display it
1140 ADRL R0,msg_dumpBlkSize ;Point at the message
1141 MOV R0,R0 ;No-op to prevent objasm bug!
1142 LDR R1,[R2,#blk__size] ;Get the block's size
1143 BL dh__writeHex ;Display it
1145 LDR R1,[R2,#blk__id] ;Load the magic ID
1146 ADRL R0,msg_dumpBlkId ;Point at the message
1147 BL dh__writeHex ;Display it nicely
1149 LDR R1,[R2,#blk__anchor] ;Find the anchor address
1150 CMP R1,#0 ;Is the block free?
1151 ADREQL R0,msg_dumpBlkFree ;Yes -- point to the message
1152 SWIEQ XOS_Write0 ;And display it on screen
1153 ADRNEL R0,msg_dumpBlkAnch ;Otherwise show anchor addr
1154 BLNE dh__writeHex ;And move on to next block
1156 LDR R14,[R2,#blk__size] ;Load the block size again
1157 ADD R14,R14,#blk__oHead+15 ;Add overhead and align
1158 BIC R14,R14,#15 ;To find the next block
1159 ADD R2,R2,R14 ;Move onto the next block
1160 B %b00 ;And skip back into the loop
1162 dh__writeHex STMFD R13!,{R2,R14} ;Save some registers
1163 SWI XOS_Write0 ;Display the string
1164 SUB R13,R13,#12 ;Make a small buffer
1165 MOV R0,R1 ;Get the number to display
1166 MOV R1,R13 ;Point to the buffer
1167 MOV R2,#12 ;The buffer size, sir
1168 SWI XOS_ConvertHex8 ;Convert it into ASCII
1169 SWI XOS_Write0 ;Display that too
1170 SWI XOS_NewLine ;Move on to a new line
1171 ADD R13,R13,#12 ;Restore the stack pointer
1172 LDMFD R13!,{R2,PC}^ ;And return to caller
1176 ;----- * Commands -----------------------------------------------------------
1178 AREA |Dynamite$$Commands|,CODE,READONLY
1180 DCB "Dynamite_HeapDump",0
1186 ;----- Data structures ------------------------------------------------------
1188 ; --- Block descriptors ---
1191 blk__anchor # 4 ;Address of block's anchor
1192 blk__size # 4 ;Block's size, as allocated
1193 blk__id # 4 ;Client's magic ID number
1194 blk__oHead # 0 ;Overhead on allocated blocks
1196 ;----- That's all, folks ----------------------------------------------------