Initial revision
[ssr] / StraySrc / Dynamite / dynamite / s / dynHeap
1 ;
2 ; dynHeap.s
3 ;
4 ; New heap management for Dynamite
5 ;
6 ; © 1994-1998 Straylight
7 ;
8 ;----- Licensing note -------------------------------------------------------
9 ;
10 ; This file is part of Straylight's Dynamite
11 ;
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)
15 ; any later version.
16 ;
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.
21 ;
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.
25
26
27 ;----- Standard header ------------------------------------------------------
28
29 GET libs:header
30 GET libs:swis
31
32 GET libs:stream
33
34 ;----- External dependencies ------------------------------------------------
35
36 GET libs:sh.fastMove
37
38 GET sh.dynArea
39 GET sh.dynTask
40 GET sh.wSpace
41
42 GET sh.messages
43
44 ;----- Macros ---------------------------------------------------------------
45
46 MACRO
47 $label DIR $reg
48 $label LDR $reg,dyn_machine ;Get the machine type
49 CMP $reg,#&A5 ;Is it a RISC PC?
50 MEND
51
52 ;----- Main code ------------------------------------------------------------
53
54 AREA |Dynamite$$Code|,CODE,READONLY
55
56 ; --- dh_alloc ---
57 ;
58 ; On entry: R0 == pointer to anchor
59 ; R1 == size to allocate in bytes
60 ; R2 == ID value to store
61 ;
62 ; On exit: R0 and R1 preserved
63 ; R2 == address of block allocated
64 ;
65 ; Use: Allocates a block from the Dynamite heap.
66
67 EXPORT dh_alloc
68 dh_alloc ROUT
69
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
82
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
86
87 LTORG
88
89 ; --- dh_free ---
90 ;
91 ; On entry: R0 == pointer to anchor of block to free
92 ;
93 ; On exit: --
94 ;
95 ; Use: Frees a Dynamite block.
96
97 EXPORT dh_free
98 dh_free ROUT
99
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
106
107 LTORG
108
109 ; --- dh_freeWithID ---
110 ;
111 ; On entry: R0 == ID of all blocks to free
112 ;
113 ; On exit: --
114 ;
115 ; Use: Frees all allocated blocks with a given ID number.
116
117 EXPORT dh_freeWithID
118 dh_freeWithID ROUT
119
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
123
124 ; --- Do the freeing job ---
125 ;
126 ; We just tonk a 0 anchor over all blocks with a matching ID.
127
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
135
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
147
148 ; --- We finished -- tidy up and return ---
149
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
153
154 LTORG
155
156 ; --- dh_blockInfo ---
157 ;
158 ; On entry: R0 == address of block anchor
159 ;
160 ; On exit: R0 preserved
161 ; R1 == address of block
162 ; R2 == size of block
163 ; R3 == block ID
164 ;
165 ; Use: Returns information about a Dynamite block
166
167 EXPORT dh_blockInfo
168 dh_blockInfo ROUT
169
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
175
176 LTORG
177
178 ; --- dh_changeID ---
179 ;
180 ; On entry: R0 == address of anchor block, or 0 for all
181 ; R1 == new ID
182 ; R2 == old ID (if R0 == 0)
183 ;
184 ; On exit: --
185 ;
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).
189
190 EXPORT dh_changeID
191 dh_changeID ROUT
192
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
196
197 ; --- Change the ID of a specific block ---
198
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
204
205 ; --- Change the ID of all blocks with ID R2 ---
206
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
212
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
223
224 98dh_changeID LDMFD R13!,{R0-R3,PC}^ ;Return to caller
225
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
229
230 LTORG
231
232 ; --- dh__checkAnchor ---
233 ;
234 ; On entry: R0 == address of anchor to check
235 ;
236 ; On exit: R1 == address of block descriptor
237 ;
238 ; Use: Ensures that a given anchor is valid.
239
240 dh__checkAnchor ROUT
241
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
251
252 LTORG
253
254 ; --- dh__ensure ---
255 ;
256 ; On entry: R0 == number of bytes required (multiple of 16)
257 ;
258 ; On exit: R0 == pointer to base of area allocated
259 ;
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.
264
265 dh__ensure ROUT
266
267 STMFD R13!,{R1-R5,R14} ;Save some registers
268 MOV R1,R0 ;Keep the size I want
269
270 ; --- Try to find some space among the free blocks ---
271
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
278
279 00dh__ensure CMP R2,R3 ;Is there more to go?
280 BGE %05dh__ensure ;No -- then extend the heap
281
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
290
291 ; --- Found a free block ---
292
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
298
299 ; --- Found a big enough space ---
300
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
306
307 ; --- Return address of this memory ---
308
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
312
313 ; --- Main size ensuring loop ---
314
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
320
321 ; --- Try to get some more pages ---
322
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
330
331 ; --- Hmm... -- try compacting the heap ---
332
333 BL dh_compact ;Try to compact the heap
334 BCC %05dh__ensure ;If it did, try again
335
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
339
340 ; --- Extend the heap and return the base address ---
341
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
350
351 LTORG
352
353 ; --- dh_reduce ---
354 ;
355 ; On entry: --
356 ;
357 ; On exit: CS if there was nothing we could do
358 ;
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
362 ; M40.
363
364 EXPORT dh_reduce
365 dh_reduce ROUT
366
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
373
374 ; --- Do search for free blocks ---
375
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
383
384 ; --- Find a free block ---
385
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
389
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
398
399 ; --- We've found a free block ---
400
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
407
408 ; --- Check for two free blocks together ---
409
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
415
416 ; --- Join two adjacent free blocks together ---
417
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...
423
424 ; --- We may be searching for the last block ---
425
426 02dh_reduce
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
431
432 ; --- There's a block to bring down ---
433
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
447
448 ; --- We need to fix up the block we moved ---
449
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
453
454 ; --- That's it -- return to caller ---
455
456 B %10dh_reduce ;Return to caller
457
458 ; --- We've reached the end of the heap ---
459 ;
460 ; Now things get a little more complicated:
461 ;
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
464 ; is compacted.
465 ;
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
470 ; end of the heap.
471
472 04dh_reduce
473 DIR R14 ;Get the heap direction
474 BGE %05dh_reduce ;Upwards -- jump ahead
475
476 CMP R8,#0 ;Was there a previous block?
477 BNE %07dh_reduce ;Yes -- compact heap then
478
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
482
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
487
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
497
498 ; --- Merge the last block with the free area ---
499
500 05dh_reduce CMP R5,R1 ;Had we reached the end?
501 BGE %90dh_reduce ;Yes -- heaps compact then
502
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
517
518 ; --- Move a block in a downwards heap ---
519
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
534
535 ; --- We need to fix up the block we moved ---
536
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
540
541 ; --- That's it -- return ---
542
543 10dh_reduce LDMFD R13!,{R0-R9,R14} ;Load back registers
544 BICS PC,R14,#C_flag ;Return with C clear
545
546 ; --- There wasn't anything to do -- we're compacted ---
547
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
553
554 ; --- Nothing could be done ---
555
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
559
560 LTORG
561
562 ; --- dh_compact ---
563 ;
564 ; On entry: --
565 ;
566 ; On exit: --
567 ;
568 ; Use: Does a full compaction of the heap.
569
570 EXPORT dh_compact
571 dh_compact ROUT
572
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
580
581 LTORG
582
583 ; --- dh__unCompact ---
584 ;
585 ; On entry: --
586 ;
587 ; On exit: --
588 ;
589 ; Use: Marks the heap as being uncompact.
590
591 dh__unCompact ROUT
592
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
600
601 LTORG
602
603 ; --- dh_lock ---
604 ;
605 ; On entry: --
606 ;
607 ; On exit: R10 corrupted (SWIs don't care about this)
608 ;
609 ; Use: Locks the heap, so that compaction entirely fails to happen.
610
611 EXPORT dh_lock
612 dh_lock ROUT
613
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
618
619 LTORG
620
621 ; --- dh_unlock ---
622 ;
623 ; On entry: --
624 ;
625 ; On exit: R10 corrupted (SWIs don't care about this)
626 ;
627 ; Use: Unlocks the heap, so that compaction can happen again, maybe.
628
629 EXPORT dh_unlock
630 dh_unlock ROUT
631
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
637
638 LTORG
639
640 ; --- dh_save ---
641 ;
642 ; On entry: R0 == mask of registers to save
643 ;
644 ; On exit: R10, R11 corrupted
645 ;
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.
650
651 EXPORT dh_save
652 dh_save ROUT
653
654 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
655
656 LDR R10,dyn_stackPtr ;Load the stack pointer
657
658 TST R0,#&03F
659 BEQ %f05
660 MOVS R11,R0,LSL #31
661 STRCS R1,[R10],#4
662 TST R0,#&3FC
663 BEQ %f00
664 MOVS R11,R0,LSL #29
665 STRMI R2,[R10],#4
666 STRCS R3,[R10],#4
667 TST R0,#&3F0
668 BEQ %f00
669 MOVS R11,R0,LSL #27
670 STRMI R4,[R10],#4
671 STRCS R5,[R10],#4
672 TST R0,#&3C0
673 BEQ %f00
674 05 MOVS R11,R0,LSL #25
675 STRMI R6,[R10],#4
676 STRCS R7,[R10],#4
677 MOVS R11,R0,LSL #23
678 STRMI R8,[R10],#4
679 STRCS R9,[R10],#4
680 00
681 STR R10,dyn_stackPtr ;Save stack pointer back
682 MOVS PC,R14 ;And return to caller
683
684 LTORG
685
686 ; --- dh_load ---
687 ;
688 ; On entry: R0 == mask of registers to load
689 ;
690 ; On exit: R10, R11 corrupted
691 ;
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.
696
697 EXPORT dh_load
698 dh_load ROUT
699
700 ; --- StrongARM friendly version 1st October 1996 [mdw] ---
701
702 LDR R10,dyn_stackPtr ;Load the stack pointer
703
704 TST R0,#&3F0
705 BEQ %f05
706 MOVS R11,R0,LSL #23
707 LDRCS R9,[R10,#-4]!
708 LDRMI R8,[R10,#-4]!
709 TST R0,#&0FF
710 BEQ %f00
711 MOVS R11,R0,LSL #25
712 LDRCS R7,[R10,#-4]!
713 LDRMI R6,[R10,#-4]!
714 TST R0,#&03F
715 BEQ %f00
716 MOVS R11,R0,LSL #27
717 LDRCS R5,[R10,#-4]!
718 LDRMI R4,[R10,#-4]!
719 TST R0,#&00F
720 BEQ %f00
721 05 MOVS R11,R0,LSL #29
722 LDRCS R3,[R10,#-4]!
723 LDRMI R2,[R10,#-4]!
724 MOVS R11,R0,LSL #31
725 LDRCS R1,[R10,#4]!
726 00
727 STR R10,dyn_stackPtr ;Save stack pointer back
728 MOVS PC,R14 ;And return to caller
729
730 LTORG
731
732 ; --- dh_extend ---
733 ;
734 ; On entry: R0 == address of block anchor
735 ; R1 == new size for block
736 ;
737 ; On exit: R0 preserved
738 ; R1 == address of block, may have moved
739 ;
740 ; Use: Changes the size of a block.
741
742 EXPORT dh_extend
743 dh_extend ROUT
744
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
752
753 LTORG
754
755 ; --- dh_midExtend ---
756 ;
757 ; On entry: R0 == address of block anchor
758 ; R1 == byte offset from block start
759 ; R2 == number of bytes to insert
760 ;
761 ; On exit: R0 preserved
762 ; R1 == address of block, may have moved
763 ;
764 ; Use: Inserts or removes bytes at a given offset into a Dynamite
765 ; heap block.
766
767 EXPORT dh_midExtend
768 dh_midExtend ROUT
769
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
774
775 ; --- To start with, some sanity checks ---
776
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
789
790 ; --- Now do the correct extend job ---
791
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
795
796 ; --- Make a block bigger ---
797 ;
798 ; We do this in 3 stages:
799 ;
800 ; * Get the amount of dead space at the end of the block, and
801 ; see if this is enough.
802 ;
803 ; * If not, gather together the free blocks immediately
804 ; following the block and add this to the dead space.
805 ;
806 ; * If we still don't have enough, we ensure a block of the
807 ; required size (plus block descriptor) and copy the data
808 ; into there.
809 ;
810 ; Registers will be used as follows:
811 ;
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
817
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
825
826 ; --- Now we must gather free blocks together nicely ---
827
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
835
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
848
849 LDMFD R13!,{R8-R10} ;Restore the stack pointer
850 B %30dh_midExtend ;And do the extend op
851
852 ; --- Not enough space in free blocks ---
853 ;
854 ; We dh__ensure enough space in the heap, and copy the whole
855 ; lot.
856
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
872
873 ; --- Set up registers for the resize op ---
874
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
878
879 ; --- Perform the block resize ---
880
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
886
887 ; --- Increase the heap size if we need to ---
888
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
898
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
903
904 ; --- Insert a free block here ---
905
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
910
911 ; --- Now split the block as required ---
912
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
919
920 ; --- We have to reduce a block ---
921
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
929
930 ; --- Now update the size and insert free block ---
931
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
939
940 ; --- Insert a free block here ---
941
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
949
950 ; --- Now everything is great ---
951
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
955
956 ; --- We failed miserably ---
957
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
961
962 LTORG
963
964 ; --- dh__move ---
965 ;
966 ; On entry: R0 == destination of movement
967 ; R1 == base of block to move
968 ; R2 == size of block to move
969 ;
970 ; On exit: --
971 ;
972 ; Use: Shunts memory around in the heap, relocating everything that
973 ; needs relocation.
974
975 dh__move ROUT
976
977 STMFD R13!,{R3,R4,R14} ;Save some registers
978 BL fastMove ;Do the memory movement
979
980 ; --- Now relocate entries on the stack ---
981
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
992
993 20dh__move LDMFD R13!,{R3,R4,PC}^ ;Return to caller
994
995 LTORG
996
997 ; --- dh_checkHeap ---
998 ;
999 ; On entry: --
1000 ;
1001 ; On exit: May return an error
1002 ;
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.
1006
1007 EXPORT dh_checkHeap
1008 dh_checkHeap ROUT
1009
1010 STMFD R13!,{R0-R7,R14} ;Save some registers
1011
1012 ; --- Start going throught the blocks ---
1013
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
1020
1021 ; --- Find a block ---
1022
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
1028
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
1037
1038 ; --- Make sure the anchor checks OK ---
1039
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
1046
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
1052
1053 ; --- Go round for more then ---
1054
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
1060
1061 50dh_checkHeap LDMFD R13!,{R0-R7,R14} ;Load registers back
1062 BICS PC,R14,#V_flag ;Return without error
1063
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
1068
1069 LTORG
1070
1071 ; --- dh_changeAnchor ---
1072 ;
1073 ; On entry: R0 == pointer to anchor for block
1074 ; R1 == address of new anchor
1075 ;
1076 ; On exit: --
1077 ;
1078 ; Use: Adjusts a block's anchor, in case it moves.
1079
1080 EXPORT dh_changeAnchor
1081 dh_changeAnchor ROUT
1082
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
1090
1091 LTORG
1092
1093 ; --- dh_dump ---
1094 ;
1095 ; On entry: --
1096 ;
1097 ; On exit: --
1098 ;
1099 ; Use: Outputs a textual description of the dynamite heap, giving
1100 ; details of each block within it.
1101
1102 dh__preDump LDR R12,[R12]
1103
1104 EXPORT dh_dump
1105 dh_dump ROUT
1106
1107 STMFD R13!,{R0-R4,R14} ;Stack some registers
1108
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
1114
1115 ; --- Display information about the heap in general ---
1116
1117 ADRL R0,msg_dumpHpBase ;Find the message
1118 MOV R1,R2 ;Get the heap base address
1119 BL dh__writeHex ;And display it
1120
1121 MOV R1,R4 ;Get the heap size
1122 ADRL R0,msg_dumpHpSize ;Find the message
1123 BL dh__writeHex ;And display it
1124
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
1128
1129 ; --- Now start on the loop ---
1130
1131 00 CMP R2,R3 ;Have we finished yet?
1132 LDMCSFD R13!,{R0-R4,PC}^ ;Yes -- then return
1133
1134 SWI OS_NewLine ;Start a new line here
1135
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
1139
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
1144
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
1148
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
1155
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
1161
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
1173
1174 LTORG
1175
1176 ;----- * Commands -----------------------------------------------------------
1177
1178 AREA |Dynamite$$Commands|,CODE,READONLY
1179
1180 DCB "Dynamite_HeapDump",0
1181 DCD dh__preDump
1182 DCD 0
1183 DCD synt_heapDump
1184 DCD help_heapDump
1185
1186 ;----- Data structures ------------------------------------------------------
1187
1188 ; --- Block descriptors ---
1189
1190 ^ 0
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
1195
1196 ;----- That's all, folks ----------------------------------------------------
1197
1198 END