4 ; Flexible memory handling (MDW)
6 ; © 1994-1998 Straylight
9 ;----- Licensing note -------------------------------------------------------
11 ; This file is part of Straylight's flex.
13 ; Flex 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)
18 ; Flex 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.
23 ; You should have received a copy of the GNU General Public License
24 ; along with Flex. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 ;----- New unified version --------------------------------------------------
29 ; I'm finally fed up of maintaining four different versions of this code.
30 ; From now on, there is only this one.
32 ; Lots of options are supported:
34 ; OPT_APCS Generate an APCS-compatible version
35 ; OPT_SAPPHIRE Generate a Sapphire-compatible version
36 ; OPT_STEEL Apply some STEEL-specific eccentricities
37 ; OPT_STANDALONE Build a standalone assembler version (default)
38 ; OPT_DUMP Generate flex_dump code
39 ; OPT_DYNAREA Generate dynamic-area handling code
40 ; OPT_STACK Generate relocation stack handling code
41 ; OPT_DLL Generate absolute address relocation for DLL code
42 ; OPT_ATEXIT Register cleanup function with `atexit' for DLL code
46 ;----- Set up some options --------------------------------------------------
68 [ :LNOT:OPT_APCS:LAND::LNOT:OPT_SAPPHIRE:LAND::LNOT:OPT_STANDALONE
70 OPT_STANDALONE SETL {TRUE}
73 ;----- Standard stuff -------------------------------------------------------
78 ;----- External dependencies ------------------------------------------------
85 GET sapphire:roVersion
91 [ OPT_DLL:LAND:OPT_APCS
95 ;----- Workspace macros -----------------------------------------------------
100 $label WSPACE $addr,$reg
118 ;----- Main code ------------------------------------------------------------
121 AREA |Sapphire$$Code|,CODE,READONLY
124 AREA |C$$Code|,CODE,READONLY
127 AREA |Straylight$$Code|,CODE,READONLY
130 ; --- flex__setslot ---
132 ; On entry: R0 == limit address of slot required, or -1 to read
134 ; On exit: R0 == actual address (after update)
135 ; R1 == limit requested (R0 on entry)
137 ; Use: Sets the application's WimpSlot to a given value. The value
138 ; is given as an address, rather than as a size, which is
139 ; the more normal way of doing things.
141 ; Since updated to cope with dynamic areas.
145 STMFD R13!,{R2-R6,R14} ;Save some registers
146 LDR R14,flex__flags ;Load interesting flags
148 TST R14,#fFlag__dynArea ;Using a dynamic area?
149 BNE %50flex__setslot ;Yes -- do different things
152 ; --- Change the WimpSlot ---
154 ; Be careful -- we may be sharing memory space with another
157 MOV R5,R0 ;Look after my argument
158 MOV R0,#14 ;Read app space value
159 MOV R1,#0 ;Read, rather than write
160 SWI XOS_ChangeEnvironment ;Read the value
161 MOV R3,R1 ;Keep hold of app space size
163 MOV R0,#0 ;Now read memory limit
164 MOV R1,#0 ;Read again, not write
165 SWI XOS_ChangeEnvironment ;Read memory limit
166 MOV R6,R1 ;Look after memory limit
168 CMP R6,R3 ;How does this shape up?
169 MOVLT R1,R3 ;If too low, extend mem limit
170 SWILT XOS_ChangeEnvironment ;Set memory limit
172 CMP R5,#-1 ;Does he want to read it?
173 MOVEQ R0,R5 ;Yes -- do that then
174 SUBNE R0,R5,#&8000 ;Otherwise work out slot size
175 MOV R1,#-1 ;Not interested in next slot
176 SWI XWimp_SlotSize ;Change the WimpSlot value
177 MOV R4,R0 ;Look after updated value
179 CMP R6,R3 ;If we changed the mem limit
180 MOVLT R1,R6 ;Put it back the way it was
181 MOVLT R0,#0 ;Setting memory limit
182 SWILT XOS_ChangeEnvironment ;Restore memory limit
184 ADD R0,R4,#&8000 ;New value of WimpSlot
185 MOV R1,R5 ;Return requested size too
186 LDMFD R13!,{R2-R6,PC}^ ;Return to caller
188 ; --- Change a dynamic area size ---
192 50flex__setslot MOV R2,R0 ;Look after address requested
193 LDR R3,flex__end ;Find the end address
194 SUBS R1,R2,R3 ;Work out the difference
195 LDR R0,flex__dynArea ;Load dynamic area handle
196 SWI XOS_ChangeDynamicArea ;Try and change the size
197 ADDGT R0,R3,R1 ;Get new limit address
198 SUBLE R0,R3,R1 ;Irritatingly positive...
199 MOV R1,R2 ;And the caller's request
200 LDMFD R13!,{R2-R6,PC}^ ;Return to caller
206 ; --- flex__fixup ---
212 ; Use: Goes off and fixes up all the anchors currently pointing at
217 STMFD R13!,{R0-R3,R14} ;Save some registers
219 ; --- Set up for the fixup loop ---
221 LDR R1,flex__base ;Find the base of the heap
222 LDR R2,flex__free ;Find end of the blocks
224 ; --- Now go through and fix things up ---
226 00flex__fixup CMP R1,R2 ;Have we reached the end yet?
227 LDMGEFD R13!,{R0-R3,PC}^ ;Return to caller if so
228 LDR R3,[R1,#flex__bkanchor] ;Find the pointer to anchor
229 CMP R3,#0 ;Is the block currently free?
230 ADDNE R14,R1,#flex__ohead ;No -- bump aver the overhead
231 STRNE R14,[R3] ;And store in the anchor
232 LDR R3,[R1,#flex__size] ;Find the block's size
233 ADD R3,R3,#flex__ohead+7 ;Add on the extra overhead
234 BIC R3,R3,#7 ;And word align the size
235 ADD R1,R1,R3 ;Bump along to the next block
236 B %00flex__fixup ;And fix that one up too
240 ; --- flex__compact ---
246 ; Use: Try to compact the heap by one iteration
248 ; We troll through the blocks to find a free one and haul
249 ; everything down a bit
253 STMFD R13!,{R0-R6,R14} ;Save some registers
255 ; --- Set up for a loop through the data ---
257 LDR R5,flex__base ;Find the beginning of flex
258 LDR R1,flex__free ;Find the end too
260 ; --- Find a free block ---
262 00flex__compact CMP R5,R1 ;Are we at the end yet?
263 BGE %10flex__compact ;Yes -- the heap is compact
265 LDR R2,[R5,#flex__bkanchor] ;Get the block's anchor addr
266 CMP R2,#0 ;Is the block free?
267 LDR R2,[R5,#flex__size] ;Get the block size
268 ADD R2,R2,#flex__ohead+7 ;Add on the overhead bytes
269 BIC R2,R2,#7 ;And word align the size
270 ADDNE R5,R5,R2 ;No -- move on to next one
271 BNE %00flex__compact ;And go round for another one
273 ; --- We've found a free block ---
275 01flex__compact ADD R6,R5,R2 ;Point to the next block
276 CMP R6,R1 ;Is that the end of the heap?
277 BGE %05flex__compact ;Yes -- reduce the free ptr
279 ; --- Check for two free blocks together ---
281 LDR R0,[R6,#flex__bkanchor] ;Does this have an anchor?
282 CMP R0,#0 ;Check if it's free
283 BNE %02flex__compact ;No -- start swapping blocks
285 ; --- Join two adjacent free blocks together ---
287 LDR R0,[R6,#flex__size] ;Yes -- get its size
288 ADD R2,R0,R2 ;Concatenate the two blocks
289 ADD R2,R2,#flex__ohead+7 ;Add on the overhead bytes
290 BIC R2,R2,#7 ;And word align the size
291 B %01flex__compact ;And check again...
293 ; --- There's a block to bring down ---
295 02flex__compact LDR R4,[R6,#flex__size] ;Get size of block to move
296 ADD R4,R4,#flex__ohead+7 ;Add the flex overhead
297 BIC R4,R4,#7 ;And word align the size
298 MOVS R2,R4 ;This is the size to move
299 MOV R0,R5 ;Where to move it to
300 MOV R1,R6 ;Where it is right now
301 BLNE fastMove ;Copy it down PDQ
303 BLNE flex__reloc ;Deal with the relocation
305 ADD R0,R5,R4 ;Point after block we moved
306 MOV R1,#0 ;Block doesn't have an anchor
307 STR R1,[R0,#flex__bkanchor] ;Store that away for later
308 SUB R1,R6,R5 ;Find the difference here
309 SUB R1,R1,#flex__ohead ;Don't count this size here
310 STR R1,[R0,#flex__size] ;Store the old size in free
312 ; --- We need to fix up the block we moved ---
314 LDR R0,[R5,#flex__bkanchor] ;Get the anchor pointer
315 ADD R1,R5,#flex__ohead ;Point to the real data
316 STR R1,[R0] ;Store client's new anchor
318 ; --- That's it -- return to caller ---
320 LDMFD R13!,{R0-R6,PC}^ ;Return to caller
322 ; --- Merge the last block with the free area ---
324 05flex__compact STR R5,flex__free ;This is the new free area
325 LDR R0,flex__chunk ;Get the machine page size
326 SUB R0,R0,#1 ;Turn into a bitmask
327 ADD R5,R5,R0 ;Align this to page boundary
328 BIC R0,R5,R0 ;And finish off the align
329 LDR R1,flex__end ;Find the end of the heap
330 CMP R0,R1 ;Are these different?
331 BLNE flex__setslot ;Yes -- free some memory
332 STRNE R0,flex__end ;Store the end away
334 ; --- That's it -- return ---
336 LDMFD R13!,{R0-R6,PC}^ ;Return to caller
338 ; --- There wasn't anything to do -- we're compacted ---
340 10flex__compact LDR R0,flex__flags
341 ORR R0,R0,#fFlag__compact ;We are now compacted
343 LDMFD R13!,{R0-R6,PC}^ ;Return to caller
347 ; --- flex_reduce ---
353 ; Use: Compacts the flex heap by one iteration.
358 STMFD R13!,{R0,R12,R14} ;Stack some registers
359 WSPACE flex__wSpace ;Find my workspace
361 ; --- Check if it's compacted ---
363 LDR R0,flex__flags ;Get my nice flags word
364 TST R0,#fFlag__compact ;Is the heap compacted?
365 BLEQ flex__compact ;No -- compact it a bit
366 LDMFD R13!,{R0,R12,PC}^ ;Return to caller now
370 ; --- flex_compact ---
376 ; Use: Completely compacts the flex heap.
381 STMFD R13!,{R0,R12,R14} ;Save some registers
382 WSPACE flex__wSpace ;Find my workspace
384 ; --- Compaction loop ---
386 00flex_compact LDR R0,flex__flags ;Get my nice flags word
387 TST R0,#fFlag__compact ;Is the heap compacted?
388 BLEQ flex__compact ;No -- compact another stage
389 BEQ %00flex_compact ;And go round again
391 ; --- The end -- return ---
393 LDMFD R13!,{R0,R12,PC}^ ;Return to caller
399 ; On entry: R0 == pointer to the flex anchor
403 ; Use: Frees a flex block allocated by flex_alloc.
408 STMFD R13!,{R0,R12,R14} ;Save some registers
411 ; --- Mark the block as being free ---
413 LDR R14,[R0] ;Get pointer to actual block
415 STR R0,[R14,#flex__bkanchor-flex__ohead]
417 ; --- Update the flags -- not compacted any more ---
419 LDR R0,flex__flags ;Get my nice flags word
420 BIC R0,R0,#fFlag__compact ;We are no longer compacted
421 STR R0,flex__flags ;Store it back
423 LDMFD R13!,{R0,R12,PC}^ ;Return to caller
429 ; On entry: R0 == pointer to a flex anchor
430 ; R1 == desired size of flex block
432 ; On exit: Sapphire: CS if no memory could be allocated, CC otherwise
433 ; APCS: R0 zero if no memory, nonzero otherwise
435 ; Use: Allocates a block in the shifting heap.
441 STMFD R13!,{R4,R5,R12,R14} ;Save some registers
443 STMFD R13!,{R0-R5,R12,R14} ;Save some registers
448 ; --- Round up the size etc. ---
450 MOV R4,R0 ;Keep the anchor pointer
451 MOV R3,R1 ;Keep the actual size wanted
452 ADD R5,R1,#flex__ohead+7 ;Add on overhead for flex
453 BIC R5,R5,#7 ;And word-align the size
455 ; --- See if there's enough space ---
457 00flex_alloc LDR R0,flex__free ;Get the free pointer
458 LDR R2,flex__end ;And the end of the block
459 SUB R1,R2,R0 ;How much room is left
460 CMP R1,R5 ;Enough for the new block
461 BGE %10flex_alloc ;Set up the block
463 ; --- Not enough room in block - try to get some more ---
465 ADD R0,R0,R5 ;Find the new slot limit
466 BL flex__setslot ;Set up the new slot
467 CMP R0,R1 ;Did we get enough
468 STRGE R0,flex__end ;Yes -- remember the new end
469 LDRGE R0,flex__free ;Find the free area again
470 BGE %10flex_alloc ;And allocate the memory
472 ; --- Can we compact the heap? ---
474 LDR R0,flex__end ;Get the old heap extent
475 BL flex__setslot ;Put the slot back again
476 LDR R0,flex__flags ;Get my current flags
477 TST R0,#fFlag__compact ;Is the heap compact?
478 BLEQ flex_compact ;No -- really compact it
479 BEQ %00flex_alloc ;And give it another go
481 ; --- We couldn't get enough memory at all ---
485 LDMFD R13!,{R4,R5,R12,PC}^ ;Restore registers
487 LDMFD R13!,{R0-R5,R12,R14} ;Restore registers
488 ORRS PC,R14,#C_flag ;Set C flag to indicate fail
491 ; --- Set up the block pointed to by R0
493 10flex_alloc STR R4,[R0,#flex__bkanchor] ;Set up the back anchor
494 STR R3,[R0,#flex__size] ;Remember size of this block
495 ADD R1,R0,#flex__ohead ;Point to real data block
496 STR R1,[R4] ;Let user know where block is
497 ADD R1,R0,R5 ;Get the new free pointer
498 STR R1,flex__free ;Store the new free ptr
500 ; --- Return to the user ---
504 LDMFD R13!,{R4,R5,R12,PC}^
506 LDMFD R13!,{R0-R5,R12,R14}
514 ; On entry: R0 == pointer to flex anchor
516 ; On exit: R0 == size of allocated block
518 ; Use: Reads the size of a flex block.
523 LDR R0,[R0] ;Get the flex block
524 LDR R0,[R0,#flex__size-flex__ohead]
529 ; --- flex_extend ---
531 ; On entry: R0 == pointer to flex anchor
532 ; R1 == new size of block to set
534 ; On exit: CS if it failed due to lack of memory, CC otherwise
536 ; Use: Alters the size of a block to the given value.
541 STMFD R13!,{R1,R2,R14}
543 ; --- Be *very* careful to preserve the flags... ---
547 LDR R1,[R1,#flex__size-flex__ohead]
553 ; We preserved the flags above, and midExtend should do
554 ; its bit to help, so we can just return with the flags
555 ; set by midExtend. Easy, no?
557 LDMFD R13!,{R1,R2,PC}
561 ; --- flex_midExtend ---
563 ; On entry: R0 == pointer to a flex anchor
564 ; R1 == `at' -- position in block to extend from
565 ; R2 == `by' -- how many bytes to extend (may be -ve)
567 ; On exit: Sapphire: CS if not enough memory, CC otherwise
568 ; APCS: R0 zero if not enough memory, nonzero otherwise
570 ; Use: Either creates a gap in a block (by>0) or deletes bytes
571 ; from a block. This is always done in such a way that the
572 ; byte originally at offset `at' is now at offset `at'+`by'.
574 EXPORT flex_midExtend
575 EXPORT flex_midextend
579 ; --- A bit of clever setting up ---
582 TEQ R2,#0 ;Move by zero bytes?
583 MOVEQ R0,#-1 ;Yes, it worked
584 MOVEQS PC,R14 ;And return
585 STMFD R13!,{R12,R14} ;Otherwise save registers
587 BIC R14,R14,#C_flag ;Clear C now
588 STMFD R13!,{R12,R14} ;Save R14 on the stack
589 TEQ R2,#0 ;Move by zero bytes?
590 LDMEQFD R13!,{R12,PC}^ ;Yes -- don't bother then
593 ; --- Save some more registers and find workspace ---
597 ; --- Find out what we have to do depending on `by' ---
599 CMP R2,#0 ;Is it +ve or -ve?
600 BGT %50flex_midExtend ;If we must extend, do that
602 ; --- We reduce the block size -- easy ---
607 STMFD R13!,{R0-R4} ;Save some more registers
609 MOV R3,R2 ;Keep the size safe
610 MOV R14,R1 ;Keep `at' safe too
612 LDR R4,[R0] ;Get the actual block ptr
613 ADD R1,R4,R14 ;Copy from `at'
614 ADD R0,R1,R2 ;Copy to `at'-|`by'|
615 LDR R2,[R4,#flex__size-flex__ohead]
616 SUBS R2,R2,R14 ;Size of block - `at'
617 BLNE fastMove ;Copy the area down
619 BLNE flex__reloc ;And relocate the stack
622 ; --- Find the new actual and logical sizes ---
624 SUB R4,R4,#flex__ohead ;Point to my actual data
625 LDR R0,[R4,#flex__size] ;Get the size of the block
626 ADD R1,R0,R3 ;Find new adjusted size
627 STR R1,[R4,#flex__size] ;This is new logical size
628 ADD R0,R0,#flex__ohead+7 ;Add the overhead to both...
629 ADD R1,R1,#flex__ohead+7 ;... of these sizes and...
630 BIC R0,R0,#7 ;... align to heap...
631 BIC R1,R1,#7 ;... granularity nicely
633 ; --- If these are different, insert a free block ---
635 SUBS R0,R0,R1 ;Is there space for free blk?
636 BLE %00flex_midExtend ;No -- just wrap up nicely
638 ; --- Insert the free block here ---
640 ADD R2,R4,R1 ;Find the free block
641 SUB R0,R0,#flex__ohead ;Subtract the overhead
642 STR R0,[R2,#flex__size] ;And store `logical' size
643 MOV R0,#0 ;This block has no owner
644 STR R0,[R2,#flex__bkanchor] ;So store a null anchor
646 ; --- There's a free block -- enable compaction ---
648 LDR R1,flex__flags ;Load my current flags
649 BIC R1,R1,#fFlag__compact ;We're not compact any more
650 STR R1,flex__flags ;Store the flags back again
652 ; --- Return to caller ---
657 LDMFD R13!,{R4,R12,PC}^
659 LDMFD R13!,{R0-R4,R12,PC}^ ;Smile, smile, smile
662 ; --- Work out how much extra space we need ---
668 STMFD R13!,{R0-R9} ;Save yet more registers
671 ; --- Take copies of the arguments ---
677 LDR R7,[R4] ;Find the actual flex block
678 SUB R7,R7,#flex__ohead ;Point to my data
679 LDR R3,[R7,#flex__size] ;Find the size of the block
681 ADD R2,R3,#7 ;Don't add in overhead
682 BIC R2,R2,#7 ;How much space in this block
683 SUB R1,R2,R3 ;R1 == dead space at the end
684 SUBS R1,R6,R1 ;R1 == extra space needed
686 ; --- Can we do it within the block? ---
688 ADDLE R3,R3,R6 ;Increase the size
689 STRLE R3,[R7,#flex__size] ;And store
690 BLE %70flex_midExtend ;Yes -- just shuffle it about
692 ; --- We need to find R1 more bytes from somewhere ---
694 ; Our strategy here is fairly simple, really (although we
695 ; could refine it a lot, I suppose).
697 ; 1. Find as many free blocks at the end of the midExtend
698 ; block as possible, join them all together and see if
701 ; 2. If not, we just flex_alloc a block of the right size
702 ; and shift everything skywards.
704 ; --- This calls for some serious register allocation ---
706 ; R1 == the amount of extra we need (round up to size)
707 ; R2 == pointer to blocks for loop
709 ; R4 == address of anchor of midExtend block
710 ; R5 == point at which we need to extend it
711 ; R6 == how much we extend it by
712 ; R7 == pointer to the actual block
713 ; R8 == pointer to next block after midExtend one
714 ; R9 == the size we've accumulated so far
716 ; --- Start the loop (I want to get off) ---
718 ADD R1,R1,#7 ;Align the size to multiple
720 MOV R9,#0 ;We haven't found any yet
721 ADD R8,R7,R2 ;Point almost to next block
722 ADD R8,R8,#flex__ohead ;Point to it properly
723 MOV R2,R8 ;Start the loop here
725 LDR R3,flex__free ;Find the free area start
727 ; --- Find free blocks now ---
730 CMP R2,R3 ;Are we at the end yet?
731 BGE %65flex_midExtend ;Oh, well -- we're stuffed
732 LDR R14,[R2,#flex__bkanchor] ;Is this block free?
733 CMP R14,#0 ;Quick check McCheck
734 BNE %65flex_midExtend ;Oh, well -- we're stuffed
735 LDR R14,[R2,#flex__size] ;Get the block's size
736 ADD R14,R14,#flex__ohead+7 ;Add on the overhead area
737 BIC R14,R14,#7 ;And align to doubleword
738 ADD R9,R9,R14 ;Accumulate the block size
739 CMP R9,R1 ;Have we got enough yet?
740 BGE %60flex_midExtend ;That's it -- we've got it
741 ADD R2,R2,R14 ;Move onto the next block
742 B %55flex_midExtend ;And check the next one
744 ; --- We got enough free blocks to save us ---
747 LDR R0,[R7,#flex__size] ;Get the current size of this
748 ADD R0,R0,R6 ;Add on the extended size
749 STR R0,[R7,#flex__size] ;And put it back again
750 BEQ %70flex_midExtend ;If perfect fit, don't create
752 ; --- Create a new free block to use up the space ---
754 ADD R0,R8,R1 ;Point to the new free block
755 SUB R2,R9,R1 ;Find out how much is over
756 SUB R2,R2,#flex__ohead ;Don't want to count that
757 STR R2,[R0,#flex__size] ;Store the size away
758 MOV R2,#0 ;This block is not used
759 STR R2,[R0,#flex__bkanchor] ;So don't give it an anchor
760 B %70flex_midExtend ;And do the shuffling about
762 ; --- There wasn't enough space there, so allocate more ---
765 MOV R9,R1 ;Look after the size we want
766 SUB R13,R13,#4 ;Create a flex anchor
767 MOV R0,R13 ;Point to it
768 SUB R1,R1,#flex__ohead ;We'll overwrite the overhead
769 BL flex_alloc ;Allocate some memory
771 CMP R0,#0 ;Did it fail?
772 ADDEQ R13,R13,#1 ;Skip past stacked anchor
773 LDMEQFD R13!,{R4-R9,R12,PC}^
775 LDMCSFD R13!,{R0-R9,R12,R14} ;If it failed, unstack...
776 ADDCS R13,R13,#1 ;Skip past stacked anchor
777 ORRCSS PC,R14,#C_flag ;... and set carry
779 LDR R3,[R13],#4 ;Get pointer to new block
781 ; --- A reminder about the registers ---
783 ; R0-R2 aren't interesting any more
784 ; R3 points flex__ohead bytes ahead of old flex__free
785 ; R4-R6 are still our arguments
786 ; R7,R8 aren't exciting any more
787 ; R9 is the amount of extra space we wanted
789 ; --- Our block may have moved -- recalculate next ptr ---
791 LDR R7,[R4] ;Point to the block
792 SUB R7,R7,#flex__ohead ;Point to the overhead area
793 LDR R8,[R7,#flex__size] ;Get the size of the block
794 ADD R8,R8,#flex__ohead+7 ;Bump over the overhead
795 BIC R8,R8,#7 ;And align to multiple of 8
797 ; --- Move all the other blocks up a bit ---
799 ADD R1,R7,R8 ;Start moving from here
800 ADD R0,R1,R9 ;Move it to here
801 SUB R2,R3,#flex__ohead ;Find the old flex__free
802 SUBS R2,R2,R1 ;Copy the right section up
805 BLNE flex__reloc ;And adjust any stacked ptrs
808 ; --- Adjust the block size and anchors ---
810 LDR R14,[R7,#flex__size] ;Get the size of this block
811 ADD R14,R14,R6 ;Add on the extension
812 STR R14,[R7,#flex__size] ;Store it back again
813 BL flex__fixup ;Fix up all the anchors again
814 ; Drop through to block shuffling
816 ; --- Create the gap in the flex block ---
819 LDR R4,[R7,#flex__size] ;Get the size of the block
820 SUB R4,R4,R6 ;Find the old length
821 ADD R1,R7,#flex__ohead ;Point to the real data
822 ADD R1,R1,R5 ;Find the `at' position
823 ADD R0,R1,R6 ;Find the `at'+|`by'| posn
824 SUBS R2,R4,R5 ;Find the length to move
825 BLNE fastMove ;Do the move
827 BLNE flex__reloc ;And adjust any stacked ptrs
832 LDMFD R13!,{R4-R9,R12,PC}^
834 LDMFD R13!,{R0-R9,R12,PC}^
841 ; On entry: R0 == pointer to dynamic area name, or zero
842 ; R1 == maximum allowed size of the area
843 ; (except Sapphire version)
847 ; Use: Initialises the flex heap for use.
858 flex_dinit STMFD R13!,{R0-R8,R12,R14}
861 [ :LNOT:OPT_STANDALONE
863 ; --- Prevent multiple initialisation ---
865 LDR R14,flex__flags ;Find my flags word
866 TST R14,#fFlag__inited ;Am I initialised yet?
867 LDMNEFD R13!,{R0-R8,R12,PC}^ ;Yes -- return right now
873 ; --- If this is Sapphire, find the options block ---
877 BL rov_init ;Work out the RISC OS version
878 BL rov_version ;Get the version
879 CMP R0,#348 ;Is this RISC OS 3.5?
880 BCC %10flex_init ;No -- skip ahead then
882 LDR R0,flex__optName ;Get the magic marker word
883 BL libOpts_find ;Try to find the options
884 BCC %10flex_init ;Not there -- skip on then
885 LDR R14,[R0,#0] ;Load the flags out
886 TST R14,#1 ;Is the dynamic area flag on?
887 BEQ %10flex_init ;No -- don't do this then
889 ; --- See if we can create a dynamic area ---
891 TST R14,#2 ;Specified area size?
892 LDRNE R5,[R0,#4] ;Yes -- load from opts block
893 MOVEQ R5,#16*1024*1024 ;16 meg maximum size
894 LDR R8,sapph_appName ;Find the application name
898 ; --- If this is APCS, then use the arguments ---
900 CMP R0,#0 ;Is a dynamic area wanted?
901 CMPNE R1,#0 ;Just check for stupidity
902 BEQ %10flex_init ;No -- then skip ahead
903 MOV R8,R0 ;Find the name pointer
904 MOV R5,R1 ;And the size requested
906 MOV R0,#129 ;Find the OS version
907 MOV R1,#0 ;Convoluted call for this...
908 MOV R2,#255 ;No idea why
909 SWI OS_Byte ;Call the operating system
910 CMP R1,#&A5 ;Is it late enough?
911 BCC %10flex_init ;No -- ignore the request
915 ; --- Create a dynamic area ---
917 MOV R0,#0 ;Create new dynamic area
918 MOV R1,#-1 ;Give me any old number
919 MOV R2,#0 ;Zero size initially
920 MOV R3,#-1 ;Don't care about base addr
921 MOV R4,#(1<<7) ;Don't let user drag the bar
922 MOV R6,#0 ;No dynamic area handler
923 MOV R7,#0 ;I wuz told to do this
924 SWI XOS_DynamicArea ;Try to create the area
925 BVS %10flex_init ;It failed -- use WimpSlot
927 ; --- Set up workspace for this ---
929 STR R3,flex__base ;Store base of the area
930 STR R3,flex__free ;The first free part
931 STR R3,flex__end ;And the end
932 STR R1,flex__dynArea ;Save dynamic area handle
934 MOV R0,#fFlag__compact + fFlag__inited + fFlag__dynArea
935 STR R0,flex__flags ;Store the appropriate flags
937 ; --- Add in tidy-up routine to delete the area ---
940 BL except_init ;We need to clear it up
941 ADR R0,flex__exit ;Point to exit handler
942 MOV R1,R12 ;Pass it my workspace
943 BL except_atExit ;Register the routine
947 ADR R0,flex_die ;Point to exit handler
948 BL atexit ;And call that
951 B %20flex_init ;And continue initialisation
955 ; --- Find out the slot size ---
957 10flex_init MOV R0,#-1 ;Read current slot size
958 BL flex__setslot ;Do the slot thing
960 ; --- Store initial heap information ---
962 STR R0,flex__base ;The start of the heap
963 STR R0,flex__free ;The first free part
964 STR R0,flex__end ;And the end
966 MOV R0,#fFlag__compact + fFlag__inited
967 ;Empty heaps is compact heaps
970 ; --- Get the page size of the machine ---
972 20flex_init SWI OS_ReadMemMapInfo ;Get page size (in R0)
973 STR R0,flex__chunk ;Store for future reference
975 ; --- Set up the flex relocation stack ---
978 ADR R14,flex__relocStk ;Point to the stack base
979 STR R14,flex__relocSP ;Save this as initial SP
982 ; --- Register the flex compactor as a postfilter ---
985 BL event_init ;Initialise event system
986 ADR R0,flex__preFilter ;Point to the prefilter
987 MOV R1,R12 ;Give it my workspace ptr
988 BL event_preFilter ;Add the filter into the list
989 ADR R0,flex__postFilter ;Point to the postfilter
990 MOV R1,R12 ;Give it my workspace ptr
991 BL event_postFilter ;Add the filter into the list
994 LDMFD R13!,{R0-R8,R12,PC}^
997 flex__optName DCB "FLEX"
1007 flex__wSpace DCD flex__sSpace
1010 ; --- flex__preFilter ---
1012 ; On entry: R0 == WIMP event mask
1013 ; R1 == pointer to event block
1014 ; R2 == time to return, or 0
1015 ; R3 == pointer to poll word if necessary
1017 ; On exit: R0,R2 maybe updated to enable idle events
1019 ; Use: Enables full idle events if the flex heap needs compacting.
1023 flex__preFilter ROUT
1025 STMFD R13!,{R14} ;Save a register
1026 LDR R14,flex__flags ;Find the flags word
1027 TST R14,#fFlag__compact ;Is the heap compacted?
1028 BICEQ R0,R0,#1 ;No -- unmask idle events
1029 MOVEQ R2,#0 ;And return immediately
1030 LDMFD R13!,{PC}^ ;Return to caller
1036 ; --- flex__postFilter ---
1038 ; On entry: R0 == WIMP reason code
1039 ; R1 == pointer to event block
1040 ; R2 == time to return or 0
1041 ; R3 == pointer to poll word or nothing really
1043 ; On exit: Everything must be preserved
1045 ; Use: Compacts the flex heap every idle event
1049 flex__postFilter ROUT
1051 CMP R0,#0 ;Is this an idle event?
1052 MOVNES PC,R14 ;No -- then return right now
1053 STMFD R13!,{R0} ;Save a register
1054 LDR R0,flex__flags ;Find the flags word
1055 TST R0,#fFlag__compact ;Is the heap compacted?
1056 LDMFD R13!,{R0} ;Restore the register's value
1057 MOVNES PC,R14 ;Return if it is
1058 B flex__compact ;Go give the heap a nudge
1066 ; --- flex_budge / flex_dont_budge ---
1072 ; Use: Nothing. Both of these do the same thing.
1075 EXPORT flex_dont_budge
1077 flex_dont_budge MOV R0,#0 ;Refuse the budge
1078 MOVS PC,R14 ;And return
1082 ; --- flex_die / flex__exit ---
1088 ; Use: Kills the dynamic area which we own.
1100 STMFD R13!,{R0,R1,R14} ;Save some registers
1102 ; --- The C library's `atexit' doesn't provide context ---
1104 [ :LNOT:OPT_SAPPHIRE
1108 ; --- Now free the dynamic area ---
1110 LDR R1,flex__dynArea ;Load the handle
1111 CMP R1,#0 ;Is it defined?
1112 MOVNE R0,#1 ;Yes -- remove it
1113 SWINE OS_DynamicArea ;Do the remove job
1114 MOVNE R0,#0 ;Now clear the handle
1115 STRNE R0,flex__dynArea ;So we don't do it again
1116 LDMFD R13!,{R0,R1,PC}^ ;Finally, return to caller
1124 ; --- flex_stackPtr ---
1126 ; On entry: R0 == 0 to read, or value to set
1128 ; On exit: R0 == old value
1130 ; Use: Either reads or writes the flex stack pointer. This sort
1131 ; of thing is useful in exception handlers etc.
1133 EXPORT flex_stackPtr
1136 STMFD R13!,{R12,R14} ;Save some registers
1137 WSPACE flex__wSpace ;Find the workspace
1138 LDR R14,flex__relocSP ;Load the current value
1139 CMP R0,#0 ;Does he want to write it?
1140 STRNE R0,flex__relocSP ;Yes -- then write it
1141 MOV R0,R14 ;Return the old value
1142 LDMFD R13!,{R12,PC}^ ;And return to caller
1148 ; On entry: R0 == value to save, for APCS
1152 ; Use: Saves some registers on the flex relocation stack. R13
1153 ; and R14 cannot be saved -- these registers are corrupted
1154 ; during this routine's execution.
1156 ; Values saved on the flex relocation stack are adjusted as
1157 ; flex moves blocks of memory around, so that they still point
1158 ; to the same thing as they did before. Obviously, values
1159 ; which aren't pointers into flex blocks may be corrupted.
1160 ; Values pointing to objects deleted (either free blocks, or
1161 ; areas removed by flex_midExtend) may also be corrupted.
1163 ; Since this routine takes no arguments, some other method has
1164 ; to be used. The method chosen is to follow the call to
1165 ; flex_save with a LDM or STM instruction containing the
1166 ; registers to be saved. This instruction is skipped by the
1167 ; routine, and thus not executed.
1169 ; Note that if you give the LDM or STM the same condition code
1170 ; as the BL preceding it, it will never be executed, since
1171 ; flex_save skips it if the condition is true and it can't be
1172 ; executed if the condition is false.
1174 ; (All the above is only true for the Sapphire version.)
1181 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
1183 STMFD R13!,{R10,R11,R12,R14} ;Save some registers away
1184 BIC R10,R14,#&FC000003 ;Clear processor flags
1185 WSPACE flex__wSpace ;Locate flex's workspace
1186 LDR R11,flex__relocSP ;Load the stack pointer
1187 LDR R10,[R10,#0] ;Load the instruction out
1189 ; --- Rather optimised code ---
1191 ; Shift two bits at a time into C and N. Leave early if
1196 MOVS R14,R10,LSL #31
1199 MOVS R14,R10,LSL #29
1204 MOVS R14,R10,LSL #27
1209 05 MOVS R14,R10,LSL #25
1212 MOVS R14,R10,LSL #23
1217 MOVS R14,R10,LSL #21
1223 ; --- Tidy up and return home ---
1225 STR R11,flex__relocSP ;Store new stack ptr
1226 LDMFD R13!,{R10,R11,R12,R14} ;And return to caller
1231 STMFD R13!,{R12,R14} ;Save registers
1232 WSPACE flex__wSpace ;Find my workspace
1233 LDR R14,flex__relocSP ;Load the current pointer
1234 STR R0,[R14],#4 ;Store the value away
1235 STR R14,flex__relocSP ;Store the stack pointer
1236 LDMFD R13!,{R12,PC}^ ;And return to caller
1246 ; On exit: Registers loaded from relocation stack as requested
1248 ; Use: Restores registers saved on flex's relocation stack. See
1249 ; flex_save for calling information and details about the
1257 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
1259 STMFD R13!,{R10,R11,R12,R14} ;Save some registers away
1260 BIC R10,R14,#&FC000003 ;Clear processor flags
1261 WSPACE flex__wSpace ;Locate flex's workspace
1262 LDR R11,flex__relocSP ;Load the stack pointer
1263 LDR R10,[R10,#0] ;Load the instruction out
1265 ; --- Rather optimised code ---
1267 ; Shift two bits at a time into C and N. Leave early if
1268 ; possible. Do it backwards, because otherwise it doesn't
1273 MOVS R14,R10,LSL #21
1274 LDRCS R14,[R11,#-4]!
1276 LDRMI R14,[R11,#-4]!
1278 MOVS R14,R10,LSL #23
1283 MOVS R14,R10,LSL #25
1288 MOVS R14,R10,LSL #27
1293 05 MOVS R14,R10,LSL #29
1296 MOVS R14,R10,LSL #31
1300 ; --- Tidy up and return home ---
1302 STR R11,flex__relocSP ;Store new stack ptr
1303 LDMFD R13!,{R10,R11,R12,R14} ;And return to caller
1308 STMFD R13!,{R12,R14} ;Save registers
1309 WSPACE flex__wSpace ;Find my workspace
1310 LDR R14,flex__relocSP ;Load the current pointer
1311 LDR R0,[R14,#-4]! ;Load the value out
1312 STR R14,flex__relocSP ;Store the stack pointer
1313 LDMFD R13!,{R12,PC}^ ;And return to caller
1319 ; --- flex__reloc ---
1321 ; On entry: R0 == destination pointer of move
1322 ; R1 == source pointer of move
1323 ; R2 == length of block to move
1327 ; Use: Relocates the flex stack after a heap operation which moved
1328 ; memory. The arguments are intentionally the same as those
1329 ; for fastMove, which should be called immediately before this
1334 STMFD R13!,{R3,R4,R14} ;Save some registers
1336 ; --- Set up initial values ---
1338 ADR R3,flex__relocStk ;Point to the flex stack base
1339 LDR R4,flex__relocSP ;Load the current stack ptr
1341 ; --- Go through all the stack entries ---
1343 00flex__reloc CMP R3,R4 ;Have we reached the end?
1344 LDMGEFD R13!,{R3,R4,PC}^ ;Yes -- return to caller
1345 LDR R14,[R3],#4 ;Load the saved value
1346 SUB R14,R14,R1 ;Subtract the source base
1347 CMP R14,R2 ;Is value within block?
1348 ADDLO R14,R14,R0 ;Yes -- add the destination
1349 STRLO R14,[R3,#-4] ;Store pointer if it changed
1350 B %00flex__reloc ;And go round for the rest
1363 STMFD R13!,{R0-R12,R14}
1370 LDR R5,[R11,#flex__base]
1371 LDR R6,[R11,#flex__free]
1372 LDR R7,[R11,#flex__end]
1379 DCB 13,10,"Heap free area: ",0
1383 DCB 13,10,"Heap end: ",0
1388 LDMGEFD R13!,{R0-R12,PC}^
1391 DCB 13,10,10,"Block address: ",0
1395 DCB 13,10," Size: ",0
1396 LDR R0,[R5,#flex__size]
1399 DCB 13,10," Anchor: ",0
1400 LDR R0,[R5,#flex__bkanchor]
1402 LDR R0,[R5,#flex__size]
1403 ADD R0,R0,#flex__ohead+7
1408 writeHex STMFD R13!,{R1,R2,R14}
1413 LDMFD R13!,{R1,R2,PC}^
1415 writeDec STMFD R13!,{R1,R2,R14}
1418 SWI XOS_ConvertInteger4
1420 LDMFD R13!,{R1,R2,PC}^
1426 ;----- Workspace ------------------------------------------------------------
1430 fFlag__inited EQU (1<<0) ;We are currently running
1431 fFlag__compact EQU (1<<1) ;The heap is compact ATM
1434 fFlag__dynArea EQU (1<<2) ;Using a dynamic area
1437 ; --- Flex block format ---
1440 flex__bkanchor # 4 ;Back-pointer to flex anchor
1441 flex__size # 4 ;Size of this flex block
1442 flex__ohead # 0 ;The flex overhead on blocks
1444 ; --- Static data ---
1446 [ :LNOT:OPT_STANDALONE
1461 flex__wSize EQU {VAR}-flex__wStart
1466 AREA |Sapphire$$LibData|,CODE,READONLY
1474 AREA |C$$zidata|,DATA,NOINIT
1475 flex__sSpace % flex__wSize
1478 ;----- That's all, folks ----------------------------------------------------