| This routine returns the HANDLE of the block containing the address
| where the given pointer points to, or H_NULL if there's no such block.
|
| This routine is NOT a duplicate of HeapPtrToHandle: unlike HeapPtrToHandle,
| PtrToHandle can meaningfully handle pointers that do not point to the
| beginning of the block.
| Kernels have provided PtrToHandle for years as "Ptr2Hd".
|
| Copyright (C) Lionel Debroux 2003 (contribution to TIGCC)
| Copyright (C) Lionel Debroux 2009 (rename, modifications & integration to GCC4TI).
|
| Equivalent C code:
/*
HANDLE PtrToHandle(void *ptr asm("d0"))
{
  HANDLE h = H_NULL;
  void *ptr2;
  while (h < 2000)
  {
     h++;
     ptr2 = HeapDeref(h);
     if (ptr >= ptr2 && (unsigned char *)ptr < (unsigned char *)ptr2 + HeapSize(h)) return h;
  }
  return H_NULL;
}
*/

.text
.even
.globl PtrToHandle
PtrToHandle:
    movem.l  %d3-%d4/%a2-%a4,-(%sp)

| Save the pointer into non-call-clobbered a2.
    movea.l  %a0,%a2
| Put the addresses of HeapDeref and HeapSize in the registers.
    movea.l  0xC8.w,%a4
    movea.l  0x96*4(%a4),%a3 | HeapDeref.
    movea.l  0x9E*4(%a4),%a4 | HeapSize.
        
| We'll check handles 1-1999 in ascending order, because:
| * HeapSize(H_NULL) crashes the calculator;
| * since handles are allocated in ascending order, pointers pointing to an
|   allocated memory area will be found more quickly.
| Checking the handles in ascending order yields a larger routine, though.
    clr.w    -(%sp)
    move.w   #2000,%d3

.L__PtrToHandle_loop:
| In theory, we should re-create the argument each time HeapDeref is called and each time HeapSize is called.
| This is because callees, among which AMS functions, _can_ destroy their stack arguments...
| Neither HeapDeref nor HeapSize has ever done so, be it on AMS (discontinued) or PedroM (which behaves the
| same way as AMS does, for compatibility and implementation sanity reasons).
    addq.w   #1,(%sp)
    cmp.w    (%sp),%d3
    beq.s    .L__PtrToHandle_not_found

| We've not run out of the heap table yet.
    jsr      (%a3) | HeapDeref.

| If this handle is not allocated, skip it, so as not to emulate the bug of HeapPtrToHandle(NULL) returning
| the index of the first free entry in the heap table (instead of H_NULL, which would make more sense, as NULL
| doesn't belong to any block). This matches the behaviour of kernel::Ptr2Hd.
    move.l   %a0,%d4
    beq.s    .L__PtrToHandle_loop

| If a2 < the lowest address of the current handle, the current handle is not the one we're looking for.
    cmpa.l   %d4,%a2
    blt.s    .L__PtrToHandle_loop

| a2 >= the lowest address of the current handle.
    jsr      (%a4) | HeapSize.
    add.l    %d0,%d4

| If a2 >= the lowest address and < the highest address, the current handle is the one we're looking for.
    cmp.l    %a2,%d4
    blt.s    .L__PtrToHandle_loop

| Found the handle.
    move.w   (%sp)+,%d0

.L__PtrToHandle_end:
    movem.l  (%sp)+,%d3-%d4/%a2-%a4
    rts

| The whole heap table was traversed, but no handle was found. Return H_NULL.
.L__PtrToHandle_not_found:
    moveq    #0,%d0
    addq.l   #2,%sp
    bra.s    .L__PtrToHandle_end
