Page 1 of 1

Common Heap foibles

Posted: Sun Dec 26, 2021 3:33 pm
by NormanDunbar
As part of an article for the next eMagazine, I'm discussing the use of heaps in SMSQ/E. Specifically the common heap - which is where I'm seeing some things I don't yet understand and which don't appear to match up to the docs (QDOS/SMS Reference Manual, 4.5, and Pennel QDOS Companion)

Summary
  • The rounding doesn't appear correct; It's not always to the next multiple of 8;
  • None of the docs mention that the block header is included in the space allocated?
  • Is the block header pointer to next free space relative?
  • The block header doesn't appear correct either;
  • Owning job Id seems always to be zero.
  • Request 1 byte, gets 32 bytes returned in D1, expected 24.
  • Request 10 bytes, also gets 32 bytes returned in D1, expected 32.
  • Request $3f2 bytes, gets $410 bytes returned in D1, expected $408.
  • Request $400 bytes, also gets $410 bytes returned in D1, expected $410.

Detail

I know that there's a 16 byte overhead included before the start of the common heap space allocated, detailing:
  • Size of the block;
  • (Relative?) pointer to the next free space in the common heap (or the address of the device driver code to free this block);
  • Owning Job Id;
  • Address of a byte to be set when this block is freed.
If I ask for 1 byte of common heap, I expect to get 8, plus an overhead of 16 giving a grand total of 24 bytes, but $20 (32) is returned in D1 as the size of the block allocated. Is there a miniumum allocation size for a block? Or is rounding to a multiple of 16 and not 8?

None of the docs I've seen specifically mention that the overhead is included in the value returned in D1 from SMS.ACHP/MT_ALLOC. Is this definitely the case? It appears to be as requesting 10 bytes also gives me $20 according to D1. (10 rounded to 16 plus 16.)

On top of the weird rounding up, the 4 long words ahead of the base of the area allocated are looking strange. I get:

-$10 Size of block = $20
-$0C Pointer to next free/dealloc code = $00
-$08 Owner job ID = $00
-$04 Address to set when block freed = $00

EDIT: Of course S*BASIC owns it, you CALLed the code, it's not in a job! Duh!

I can understand the address to set being zero, but there doesn't seem to be a (relative?) pointer to the next free block. Unless the pointer is from the end of the allocated block? (Which I doubt!)

What am I not understanding?

QPC on Linux Mint 20.3 Under wine. 64 bits.


Cheers,
Norm.

Re: Common Heap foibles

Posted: Sun Dec 26, 2021 11:33 pm
by mk79
The rounding doesn't appear correct; It's not always to the next multiple of 8;
mem_alhp has an allocation unit of 8 bytes, common heap allocations 16 bytes. If that would leave a 16 byte gap it can also be 32. In any case this is a implementation detail not to be relied upon.
None of the docs mention that the block header is included in the space allocated?
That is a bit weird, yes. But personally I've never checked the value, either you got the memory you wanted or you didn*t.
Is the block header pointer to next free space relative?
Yes.
I can understand the address to set being zero, but there doesn't seem to be a (relative?) pointer to the next free block. Unless the pointer is from the end of the allocated block? (Which I doubt!)
The free block pointer is only valid on free blocks (to get to the next)! It's a linked list of free blocks so to say, with sys_chpf as an anchor.

Here's a little utility I wrote to debug heap and memory corruption problems, it dumps the current heap structure to a file.

Code: Select all

100 INPUT#0,'File> ';File$
110 OPEN_NEW#3,File$
120 PRINT#3,"Freemem :"; FREE_MEM/1024; "kb"
130 PRINT#3\\"Common heap"\
140 common -1
150 PRINT#3\\"TPA"\
160 TPA -1
170 CLOSE#3
990 :
1000 DEFine PROCedure common(jobID)
1010 adr = PEEK_L(HEX('28004'))
1020 chpfr = HEX('28004') + PEEK_L(HEX('28008'))
1030 endHeap = PEEK_L(HEX('2800C'))
1040 heap(jobID)
1050 END DEFine common
1060 :
1070 DEFine PROCedure TPA(jobID)
1080 adr = PEEK_L(HEX('28014'))
1090 chpfr = HEX('28014') + PEEK_L(HEX('28018'))
1100 endHeap = PEEK_L(HEX('28020'))
1110 heap(jobID)
1120 END DEFine TPA
1130 :
1140 DEFine PROCedure heap(jobID)
1150 :
1160 ch% = 3
1170 flag = 0
1180 PRINT#ch%,CHR$(10)&'address  '!'length'!'driver'!'ownr'!'rflag    '!'name'
1190 :
1200 REPeat heaploop
1210   IF adr >= endHeap: EXIT heaploop
1220   CHP_LEN = PEEK_L(adr)
1230   CHP_JOB = PEEK_W(adr + 10)
1240   IF jobID >= 0 AND jobID <> CHP_JOB: GO TO 1350
1250   CHP_DRIVR = PEEK_L(adr + 4)
1260   CHP_OWNER = PEEK_L(adr + 8)
1270   CHP_RFLAG = PEEK_L(adr + 12)
1280   PRINT#ch%,HEX$(adr,32)&' '!HEX$(CHP_LEN,24)!HEX$(CHP_DRIVR,24)!HEX$(CHP_JOB,16)!HEX$(CHP_RFLAG,32);
1290   IF chpfr = adr THEN
1300     PRINT#ch%,'  free': chpfr = chpfr + CHP_DRIVR
1310   ELSE
1320     PRINT#ch%,'  '&JOB$(CHP_OWNER)
1330   END IF
1340   flag = 1
1350   adr = adr + CHP_LEN
1360 END REPeat heaploop
1370 :
1380 IF NOT flag: PRINT#ch%,'Nothing found': ELSE PRINT#ch%,'Finished'
1390 END DEFine heap

Re: Common Heap foibles

Posted: Tue Dec 28, 2021 4:18 pm
by NormanDunbar
Thanks Marcel, I'll hopefully get a better look at your reply when MrsD stops giving me stuff to do! :(

Cheers,
Norm.