; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; s.growshrink

; heap block can move when resized, so make sure pointers fixed-up

; change number of pages in slot (grow/shrink)

; entry:
;     R0 = 2 (reason code 2)
;     R1 = new number of pages required
;     R2 = handle
;
; exit:
;     R1 = new number of pages actually achieved
;     R2 = 0 if AMB handle freed (else preserved)
;     R3 = old number of pages

growshrink

        Push    "R0,R2,R4,R5,LR"

;  Debug AMB,"growshrink ",r0,r1,r2

        MOV     R5,#AbsMaxAppSize
        SUB     R5,R5,#ApplicationStart
        MOV     R5,R5,LSR #Log2PageSize   ;R5 = absolute max app pages
        CMP     R1,R5
        MOVGT   R1,R5

      [ ValidateAMBHandles
        ;validate handle
        LDR     R0,AMBNhandles
        CMP     R2,R0
        BGT     badsghandle
        CMP     R2,#1
        BLT     badsghandle
      ]

        LDR     R0,AMBNodeHandles   ; R0 -> handles array
        LDR     R2,[R0,R2,LSL #2]   ; R2 -> node

      [ ValidateAMBHandles
        ;check we have a proper id for node
        LDR     R3,=AMBMagicNodeID
        LDR     LR,[R2,#AMBNode_id]
        CMP     LR,R3
        BNE     badsghandle
      ]

        LDR     R3,[R2,#AMBNode_Npages]
        CMP     R1,R3
        Pull    "R0,R2,R4,R5,LR",EQ    ; done if no change
        BEQ     SLVK

        MOV     R5,R3                  ; R5 := old no. pages
        BLT     gs_shrink

;grow
        BL      grwshr_node            ; grow node itself first
        BVS     gs_done                ; failed node grow is not error - means grow by 0
        BL      growpages              ; grow the slot (MMU mapping)
        STRVS   R0,[SP]
        Pull    "R0,R2,R4,R5,LR",VS
        BVS     SLVK_SetV
        B       gs_done
gs_shrink
        BL      shrinkpages            ; shrink the slot (MMU mapping) first
        BLVC    grwshr_node            ; shrink node itself
        STRVS   R0,[SP]
        Pull    "R0,R2,R4,R5,LR",VS
        BVS     SLVK_SetV
gs_done
        MOV     R3,R5                    ;old no. of pages
        CMP     R2,#0                    ;also clears V
        MOVEQ   R1,#0
        LDRNE   R1,[R2,#AMBNode_Npages]

        CMP     R2,#0                    ;0 for freed node
        STREQ   R2,[SP,#4]               ;poke freed handle to saved R2

        Pull    "R0,R2,R4,R5,LR"
        B       SLVK


;handle grow/shrink of node itself
;entry:
;   R1 = new no. of pages
;   R2 -> node
;   R5 = old no. of pages
;exit:
;   R2 -> node (may be moved)
grwshr_node
        Push    "R0,R3,R4,LR"
        CMP     R1,#0
        BEQ     grwshrn_free                     ;shrunk to nothing
        MOV     R3,#(AMBNode_pages - AMBNode_id) ;new size excluding page list
        ADD     R3,R3,R1,LSL #2                  ;new size
        MOV     R4,R2                            ;so we can check for moved block
        BL      AMB_BlockResize
        MOVVS   R2,R4                            ;in case block ptr not preserved on error
        BVS     grwshrn_done
;fix-up links if block has moved
        CMP     R4,R2
        BEQ     grwshrn_done

        LDR     R3,[R2,#AMBNode_prev]
        STR     R2,[R3,#AMBNode_next]   ;fix prev_next
        LDR     R3,[R2,#AMBNode_next]
        STR     R2,[R3,#AMBNode_prev]   ;fix next_prev
        LDR     R3,AMBMappedInNode
        CMP     R3,R4
        STREQ   R2,AMBMappedInNode      ;fix MappedInNode if it matches
        LDR     R3,[R2,#AMBNode_handle]
        LDR     R4,AMBNodeHandles
        STR     R2,[R4,R3,LSL #2]       ;fix node address in handle array

grwshrn_done
        STRVS   R0,[SP]
        Pull    "R0,R3,R4,PC"

grwshrn_free
        LDR     R0,AMBNodeHandles
        LDR     R4,[R2,#AMBNode_handle]
;put handle on free list
        LDR     R3,[R0]            ;current first free handle
        STR     R3,[R0,R4,LSL #2]  ;attach
        STR     R4,[R0]            ;new first free handle

;remove node from list
        LDR     R0,[R2,#AMBNode_prev]   ; R0 -> prev_node
        LDR     R3,[R2,#AMBNode_next]   ; R3 -> next_node
        STR     R3,[R0,#AMBNode_next]   ; prev_next := next_node
        STR     R0,[R3,#AMBNode_prev]   ; next_prev := prev_node
        LDR     R0,AMBNtasks
        SUB     R0,R0,#1
        STR     R0,AMBNtasks

;if this is the mapped-in node, then nothing is now mapped in
        LDR     R0,AMBMappedInNode
        CMP     R0,R2
        MOVEQ   R0,#0
        STREQ   R0,AMBMappedInNode

        BL      AMB_BlockFree
        MOVVC   R2,#0
        B       grwshrn_done

        LTORG


      [ ValidateAMBHandles
badsghandle
        Pull    "R0,R2,R4,R5,LR"
        B       badhandle
      ]


    END