       TTL   => ChangeDyn

;******************************************************************************
; ChangeDynamic SWI
; In  : R0 =  0 => System Heap,
;             1 => RMA
;             2 => Screen
;             3 => Sprite area
;             4 => Font cache
;       R1 = no of bytes to change by
;
; Out : V set if CAO in AplWork or couldn't move all the bytes requested.
;       R1 set to bytes moved.
;******************************************************************************

ChangeDyn_AplSpace   * -1
ChangeDyn_SysHeap    * 0
ChangeDyn_RMA        * 1
ChangeDyn_Screen     * 2
ChangeDyn_SpriteArea * 3
ChangeDyn_FontArea   * 4
ChangeDyn_RamFS      * 5
ChangeDyn_MaxArea    * 5

       GET     &.Hdr.Font

ChangeDynamicSWI ROUT
       Push   "R0, r2-R6, R9, r10, lr"     ; r10 is workspace

       CMP     r0, #ChangeDyn_MaxArea
       MOVLS   r10, #0
       LDRLS   r10, [r10, #IRQsema]
       CMPLS   r10, #0
       BHI     noboff_IRQgoing

       CMP     r0, #ChangeDyn_RamFS
       BEQ     CheckRAMFSChangeOK
AllowRAMFSChange

       MOV     R6, #0
       LDR     R6, [R6, #Page_Size]
       SUB     R12, R6, #1
       ADD     R1, R1, R12
       BICS    R1, R1, R12                 ; round up to nearest page.
       MOVEQ   r11, r1                     ; area
       MOVEQ   r10, #0                     ; amount moved
       BEQ     CDS_PostService             ; zero pages!

;  IF R1<0 THEN source := R0, dest := ApplWork
;          ELSE source := ApplWork, dest := R0

       MOVMI   R12, #ChangeDyn_AplSpace    ; dest := ApplWork
       MOVMI   R11, R0                     ; source := R0
       MOVPL   R12, R0                     ; dest := R0
       MOVPL   R11, #ChangeDyn_AplSpace    ; source := ApplWork
       RSBMI   R1, R1, #0

; amount movable = current size(source) - shrink limit(source)

       CMP     r11, #ChangeDyn_SpriteArea
       LDREQ   r10, [r11, #SpriteSize-ChangeDyn_SpriteArea]
       TEQEQ   r10, #0                 ; May have no sprite area ...
       MOVEQ   r5, #0                  ; Shrink limit := 0
       BEQ     gotsizeandshrink

; need to lock the heap if we are shrinking the SysHeap or the RMA
; lock by setting the heap end to be the same as the current base;
; this ensures that no claims will use memory about to disappear

       ADRL     R10, Current_Size+4
       LDR     R10, [R10, R11, LSL #2]     ; @current size(source)

       CMP     r11, #ChangeDyn_RMA
       CMPNE   r11, #ChangeDyn_SysHeap
       BNE     notaheap_bargeahead
       MOV     lr, pc                      ; save I_bit, EQ

       TEQP    pc, #SVC_mode + I_bit
       LDR     r5, [r10]                   ; current size
       LDR     r2, [r10, #hpdbase-hpdend]
       STR     r2, [r10]
       MOV     r10, r5
       STR     r10, [stack, #4*7]
       TEQP    lr, #0                      ; restore state inc EQ state
notaheap_bargeahead
       LDRNE   R10, [R10]

       CMP     r11, #ChangeDyn_FontArea
       MOVEQ   r5, r1
       MOVEQ   r1, #-1
       MOVEQ   r2, #0                      ; in case font manager dead
       SWIEQ   XFont_ChangeArea
       MOVEQ   r1, r5                      ; preserve r1
       MOVEQ   r5, r2
       ADRNE   R5, Shrink_Limits+4
       LDRNE   R5, [R5, R11, LSL #2]
       LDRNE   R5, [R5]

       CMP     R11, #ChangeDyn_SpriteArea
       CMPEQ   R5, #saExten             ; if no sprites defined, can delete hdr
       MOVEQ   R5, #0

gotsizeandshrink
       SUB     R10, R10, R5                ; amount moveable

       CMP     R12, #ChangeDyn_Screen      ;  IF dest = screen
       BNE     %FT01

 ; THEN amount movable := MIN amount moveable, max screen - current size
       VDWS    R5
       LDR     R4, [R5, #TotalScreenSize]

       MOV     R5, #0
       LDR     R5, [R5, #RAMLIMIT]
       CMP     R5, #512*1024
       MOV     R5, #480*1024
       SUBEQ   R5, R5, #64*1024
       SUBEQ   R5, R5, R6                  ; 480K-64K-!Page_Size

       SUB     R5, R5, R4                  ; max screen - curr size
       CMP     R5, R10
       MOVLT   R10, R5

; R10 is now the amount we can move
;  IF removing from ApplWork AND amount moveable < size requested
;    Then Error NotAllMoved

01     CMP     R11, #ChangeDyn_AplSpace    ; source = AplWork?
       BNE     %FT03
       CMP     R10, R1
       BGE     %FT03                       ; can move all reqd

noboff_IRQgoing
       ADR     R0, ErrorBlock_ChDynamNotAllMoved
ChangeDynamic_Error
       MOV     r10, #0
       STR     R0, [stack]
       LDR     lr, [stack, #4*8]
       ORR     lr, lr, #V_bit
       STR     lr, [stack, #4*8]
CDS_PostServiceWithRestore
       BL      testrestoreheapend
       B       CDS_PostService
       MakeErrorBlock ChDynamNotAllMoved

testrestoreheapend
       CMP     r11, #ChangeDyn_SysHeap
       CMPNE   r11, #ChangeDyn_RMA
       LDREQ   r2, [stack, #4*7]
       ADREQ   r3, Current_Size + 4
       LDREQ   r3, [r3, r11, LSL #2]
       STREQ   r2, [r3]
       MOV     pc, lr

UserMemStartAddr & UserMemStart

Shrink_Limits                                  ; locations to look at
     &    UserMemStartAddr                     ; AplWork - unfudged
     &    SysHeapStart + :INDEX: hpdbase       ; SysHeap
     &    RMAAddress + :INDEX: hpdbase         ; RMA
     &    VduDriverWorkSpace + ScreenSize      ; Screen
     &    SpriteSpaceAddress + saFree          ; Sprites
MinRamFSSize  ; next two are still in table
     &    0                                    ; Fonts not needed
     &    MinRamFSSize                         ; RAMFS

03     CMP      R10, R1                    ; if can move more than asked for
       MOVGT    R10, R1                    ; then move requested amount
       BGE      %FT06

; moving less than asked for: set up an error for exit
       ADR     R0, ErrorBlock_ChDynamNotAllMoved
       STR     R0, [stack]
       LDR     R0, [stack, #4*8]
       ORR     R0, R0, #V_bit
       STR     R0, [stack, #4*8]
       SUB     R0, R6, #1                  ; and make amount moveable
       BICS    R10, R10, R0                ; a pagesize multiple
       BEQ     CDS_PostServiceWithRestore

; IF CAO in ApplWork AND UpCall not claimed THEN Error ChDynamCAO

06     MOV     R2, #0
       LDR     R3, [R2, #AplWorkSize]
       LDR     R2, [R2, #Curr_Active_Object]
       CMP     R2, R3
       BHI     %FT04
       MOV     R0, #UpCall_MovingMemory :AND: &FF
       ORR     R0, R0, #UpCall_MovingMemory :AND: &FFFFFF00
       MOV     R1, R10
       SWI     XOS_UpCall
       CMP     R0, #UpCall_Claimed
       BEQ     %FT05

       ADR     R0, ErrorBlock_ChDynamCAO
       B       ChangeDynamic_Error
       MakeErrorBlock ChDynamCAO

; IF service call claimed Then Error AplWSpaceInUse

04     MOV     R1, #Service_Memory   ; anybody using AplWork?
       CMP     r11, #0
       RSBLT   r0, r10, #0           ; aplwork is source: amount -ve
       MOVGE   R0, R10               ; amount that will get moved
       BL      Issue_Service
       CMP     R1, #Service_Serviced
       BNE     %FT05

       ADR     R0, ErrorBlock_AplWSpaceInUse
       B       ChangeDynamic_Error
       MakeErrorBlock AplWSpaceInUse

; Right! R10 is amount of memory we will move,
; R11 is the source
; R12 is the destination

05     CMP     r11, #ChangeDyn_FontArea
       LDREQ   r1, [r11, #FontCacheSize-ChangeDyn_FontArea]
       SUBEQ   r1, r1, r10            ; new size
       SWIEQ   XFont_ChangeArea

; remove the cursors if screen moving: might flash during modechange wrch

       TEQP    PC, #SVC_mode+I_bit
       CMP     R11, #ChangeDyn_Screen
       CMPNE   R12, #ChangeDyn_Screen
       SWIEQ   XOS_RemoveCursors

       CMP     r11, #ChangeDyn_Screen
       RSBEQ   r0, r10, #0
       BLEQ    RemovePages

; calculate addresses of blocks

       MOV     R9, #0
       LDR     R9, [R9, #MEMC_CR_SoftCopy]

       MOV     R3, R11
       BL      GetBlockEndSource
       MOV     R0, R3                  ; R0 := blockend(source)

       CMP     R12, #ChangeDyn_Screen
       BEQ     ExtendScreen            ; dest=screen: perversion needed

       MOV     R3, R12
       BL      GetBlockEnd             ; R3 := blockend(dest)

; move memory: R10 bytes from R0 backwards to R3 forwards
       MOV     R1, #0
10     SUB     R0, R0, R6
       Push    r11
       CMP     r12, #ChangeDyn_FontArea
       CMPNE   r12, #ChangeDyn_RamFS
       MOVEQ   r11, #3                 ; font area/ramfs protected
       MOVNE   r11, #0                 ; nowt else is
       BL      MoveCAMatR0toR3
       Pull    r11
       BVS     camfucked
       ADD     R1, R1, R6
       ADD     R3, R3, R6

       CMP     R1, R10
       BNE     %BT10

       CMP     R11, #ChangeDyn_Screen
       BNE     %FT07

; source=screen: need to shuffle rest of screen down.

       VDWS    R5
       LDR     R5, [R5, #TotalScreenSize]
       MOV     R3, #32*1024*1024
11     SUB     R0, R0, R6
       SUB     R3, R3, R6
       Push   "R5"
       MOV     r11, #0                ; no prot
       BL      MoveCAMatR0toR3
       Pull   "R5"
       BVS     camfucked
       SUBS    R5, r5, r6
       BGT     %BT11
       MOV     r11, #ChangeDyn_Screen

07
; now need to restore sizes if we have locked a heap

       BL      testrestoreheapend

; update object sizes: current size(dest)   +:= R10
;                      current size(source) -:= R10

       MOV     R4, #0                   ; remember for later
       LDR     R4, [R4, #SpriteSize]

       ADR     R0, Current_Size+4
       CMP     R11, #ChangeDyn_Screen  ; don't update TotalScreenSize
       LDRNE   R2, [R0, R11, LSL #2]
       LDRNE   R3, [R2]
       SUBNE   R3, R3, R10
       STRNE   R3, [R2]
       CMP     R12, #ChangeDyn_Screen  ; don't update TotalScreenSize
       LDRNE   R2, [R0, R12, LSL #2]
       LDRNE   R3, [R2]
       ADDNE   R3, R3, R10
       STRNE   R3, [R2]

       CMP     R11, #ChangeDyn_SpriteArea ; watch out for sprite area creation
       CMPNE   R12, #ChangeDyn_SpriteArea ; or deletion
       BNE     %FT00

       MOV     R1, #0                   ; used later also!!
       LDR     R3, [R1, #SpriteSize]
       CMP     R3, #0                   ; if sprite area deleted
       LDRNE   R3, =SpriteSpaceAddress  ; tell the vdu drivers
       MOV     R14, #VduDriverWorkSpace
       STR     R3, [R14, #SpAreaStart]
       BEQ     %FT00                    ; and don't touch non-existent memory

       CMP     R4, #0                   ; if area was null,
       LDMNEIB R3, {R4-R6}              ; (skip size)
       MOVEQ   R4, #0                   ; initialise variables if was null
       MOVEQ   R5, #saExten
       MOVEQ   R6, #saExten
       LDR     R0, [R1, #SpriteSize]    ; set up earlier
       STMIA   R3, {R0,R4-R6}           ; stash new set of variables
00
       MOV     R0, #0
       LDR     R2, [R0, #AplWorkSize]
       STR     R2, [R0, #MemLimit]    ; MemLimit := AplWorkSize

       CMP     R12, #ChangeDyn_Screen
       MOVEQ   R0, R10
       BLEQ    InsertPages

       CMP     r11, #ChangeDyn_Screen
       CMPNE   r12, #ChangeDyn_Screen
       SWIEQ   XOS_RestoreCursors

       CMP     R12, #ChangeDyn_FontArea
       LDREQ   r1, [r12, #FontCacheSize-4]
       SWIEQ   XFont_ChangeArea

       CMP     r11, #ChangeDyn_RamFS
       CMPNE   r12, #ChangeDyn_RamFS
       BEQ     reinitialise_RAMFS

CDS_PostService
       MOV     r1, #Service_MemoryMoved
       MOV     r0, r10                ; amount moved
       MOVS    r2, r11                ; which way was transfer?
       RSBLT   r0, r0, #0             ; APLwork was source
       MOVLT   r2, r12                ; r2 = area indicator
       BL      Issue_Service

       MOV     r1, r10                ; amount moved
       Pull   "R0, R2-R6, R9, r10, lr"
       ExitSWIHandler

Current_Size
     &    AplWorkSize                          ; AplWork
     &    SysHeapStart + :INDEX: hpdend        ; SysHeap
     &    RMAAddress + :INDEX: hpdend          ; RMA
     &    VduDriverWorkSpace + TotalScreenSize ; Screen
     &    SpriteSize                           ; sprites
     &    FontCacheSize                        ; fonts
     &    RAMDiscSize                          ; RAMFS

GetBlockEndSource
       CMP     r3, #ChangeDyn_RMA
       CMPNE   r3, #ChangeDyn_SysHeap
       BNE     GetBlockEnd
       ADR     r4, StartAddrs+4
       LDR     r3, [r4, r3, LSL #2]
       LDR     r4, [stack, #4*7]
       ADD     r3, r3, r4             ; start + size = end
       MOV     PC, lr

GetBlockEnd   ; R3 is area to get end of: return address in R3
       MOV     r1, r3
       ADR     r4, StartAddrs+4
       LDR     r3, [r4, r3, LSL #2]
       CMP     R1, #ChangeDyn_Screen          ; screen ?
       MOVEQ   PC, lr
       ADR     R4, Current_Size+4
       LDR     R4, [R4, R1, LSL #2]
       LDR     R4, [R4]
       ADD     R3, R3, R4             ; start + size = end
       MOV     PC, lr

StartAddrs
     &    0                                    ; AplWork
     &    SysHeapStart                         ; SysHeap
     &    RMAAddress                           ; RMA
     &    32*1024*1024                         ; Screen
     &    SpriteSpaceAddress                   ; sprites
     &    FontCacheAddress                     ; fonts
     &    RAMDiscAddress                       ; RAMFS

ExtendScreen

; screenpos -:= R10 (move all current blocks down)

       MOV     R2, #0
       VDWS    R5
       LDR     R5, [R5, #TotalScreenSize]
       RSB     R3, R5, #32*1024*1024
       SUB     R3, R3, R10           ; where new screen start is
20     MOV     r11, #0
       BL      Call_CAM_Mapping
       ADD     R2, R2, #1
       ADD     R3, R3, R6
       SUBS    R5, R5, R6
       BNE     %BT20

; R0 is the end of AplWork
       Push   "R7, R8, R10"
22
       Push   "R2, R3"
       ADRL    r3, PageShifts-1
       LDRB    r3, [r3, r6, LSR #12]
       MOV     r3, r2, LSL r3        ; r3 = pagesize*r2
       ADD     r3, r3, #32*1024*1024 ; physram addr of next screen block

       SUB     R0, R0, R6            ; address of last Apl block

       MOV     R1, #Service_ClaimFIQ ; we may be moving FIQ workspace
       BL      Issue_Service
       ADD     R1, R0, R6            ; end marker

; copy R6 bytes from nextscreenblock to last apl block; the last
; apl block MUST NOT be doubly mapped.

21     LDMIA   R3!, {R2, R4, R5, R7, R8, R10, R11, R12}
       STMIA   R0!, {R2, R4, R5, R7, R8, R10, R11, R12}
       CMP     R0, R1
       BLT     %BT21

       LDR     r2, [stack]
       SUB     R0, R0, R6
       MOV     R3, #CamEntries
       LDR     R3, [R3, R2, LSL #2]  ; curr addr of next screen block
       MOV     r11, r3, LSR #28      ; protection level
       BIC     r3, r3, #&F0000000

       BL      MoveCAMatR0toR3       ; last aplblock := nextscreenblock
       Pull   "r2, r3"
       BVS     camfucked2
                                     ; entry no in r2, logaddr in r3
       MOV     r11, #0
       BL      Call_CAM_Mapping      ; nextscreenblock moves into place
       MOV     R1, #Service_ReleaseFIQ
       BL      Issue_Service
       ADD     R3, R3, R6
       ADD     R2, R2, #1
       CMP     R3, #32*1024*1024
       BNE     %BT22

       Pull   "R7, R8, R10"
       MOV     R11, #ChangeDyn_AplSpace ; source was AplWork
       MOV     R12, #ChangeDyn_Screen   ; dest was screen
       B       %BT07

camfucked2
       Pull   "R7, R8, R10"
camfucked
       STR     R0, [stack]
       Pull   "R0, R2-R6, R9, lr"
       B      SLVK_SetV

MoveCAMatR0toR3
; BangCamUpdate takes entry no in R2, logaddr to set to in R3
; PPL in r11
; corrupts R4
; so find R2, call it!

      MOV      R4, #CamEntries
      MOV      R2, #NoOfCamEntries
06    LDR      R5, [R4, R2, LSL #2]
      BIC      r5, r5, #&F0000000
      CMP      R5, R0
      BEQ      Call_CAM_Mapping
      SUBS     R2, R2, #1
      BGE      %BT06

      ADR      R0, CamMapBroke
      ORRS     PC, lr, #V_bit

CamMapBroke
      &        0
      =        "!!!! CAM Map Corrupt !!!!", 0
      ALIGN
Call_CAM_Mapping
      Push   "R0, R1, R6, lr"
      BL      BangCamUpdate
      Pull   "R0, R1, R6, PC"

;........................................
; RAMFS bashing

CheckRAMFSChangeOK
      Push   "r0-r5"
      MOV     r0, #5
      ADR     r1, ramcolondollardotstar
      SWI     XOS_File
      CMPVC   r0, #0
      Pull   "r0-r5"
      BVS     AllowRAMFSChange             ; ramfs not present
      BEQ     AllowRAMFSChange             ; ramfs empty
      ADR     r0, ErrorBlock_RAMFsUnchangeable
      B       ChangeDynamic_Error
      MakeErrorBlock RAMFsUnchangeable

ramcolondollardotstar = "ram:$.*",0
ramfsname = "ramfs",0

      ALIGN

reinitialise_RAMFS
      Push   "r0-r6"
      MOV     r0, #ModHandReason_EnumerateROM_Modules
      MOV     r1, #0
      MOV     r2, #-1
look_for_RAMFS
      SWI     XOS_Module
      BVS     OKtoreinitRAMFS        ; can't find it: may be in ram
      ADR     r5, ramfsname
nameloop
      LDRB    r6, [r3], #1
      CMP     r6, #" "
      BLE     foundramfs
      LowerCase r6, lr
      LDRB    lr, [r5], #1
      CMP     lr, r6
      BEQ     nameloop
      B       look_for_RAMFS

foundramfs
      CMP     r4, #-1
      BNE     OKtoreinitRAMFS
      Pull   "r0-r6"
      B       CDS_PostService

OKtoreinitRAMFS
      MOV     r0, #ModHandReason_ReInit
      ADR     r1, ramfsname
      SWI     XOS_Module
      Pull   "r0-r6"
      B       CDS_PostService

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r0 = area number

; Out   r0 = address of area
;       r1 = current size of area

ReadDynamicArea ROUT

        CMP     r0, #ChangeDyn_MaxArea
        ADRHIL  r0, ErrorBlock_BadModuleReason
        BHI     SLVK_SetV

        CMP     r0, #ChangeDyn_Screen    ; screen?
        ADR     r10, Current_Size+4
        ADR     r11, StartAddrs+4
        LDR     r1, [r10, r0, LSL #2]
        LDR     r1, [r1]
        LDR     r0, [r11, r0, LSL #2]
        SUBEQ   r0, r0, r1               ; screen goes wackbords
        B       SLVK

; *************************************************************************
; User access to CAM mapping
; ReadMemMapInfo:
; returns R0 = pagsize
;         R1 = number of pages in use  (= R2 returned from SetEnv/Pagesize)
; *************************************************************************

ReadMemMapInfo_Code
      MOV      R10, #0
      LDR      R0, [R10, #Page_Size]
      LDR      R1, [R10, #RAMLIMIT]    ; = total memory size
      ADRL     R11, PageShifts-1
      LDRB     R11, [R11, R0, LSR #12]
      MOV      R1, R1, LSR R11
      ExitSWIHandler

; ************************************************************************
; SWI ReadMemMapEntries: R0 pointer to list.
;  Entries are three words long, the first of which is the CAM page number.
;  List terminated by -1.
; Returns pagenumber (unaltered)/address/PPL triads as below
; ************************************************************************

ReadMemMapEntries_Code  ROUT
      MOV      R10, R0
01    LDR      R12, [R10], #4
      CMP      R12, #NoOfCamEntries
      ExitSWIHandler HI
      MOV      r11, #CamEntries
      LDR      r11, [r11, r12, LSL #2]
      MOV      r12, r11, LSR #28          ; PPL
      BIC      r11, r11, #&F0000000
      STMIA    r10!, {r11, r12}
      B        %BT01

;**************************************************************************
; SWI SetMemMapEntries: R0 pointer to list of CAM page/address/PPL triads,
;  terminated by -1.
; Any address > 32M means "put the page out of the way"
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SetMemMapEntries_Code  ROUT

      Push    "R0-R4, R6, R9, lr"

      MOV      r12, r0
; BangCamUpdate takes entry no in R2, logaddr to set to in R3
; R9 current MEMC
; corrupts R4

      MOV      R9, #0
      LDR      R9, [R9, #MEMC_CR_SoftCopy]
01    LDR      R2, [r12], #4
      CMP      R2, #NoOfCamEntries
      BHI      %FT02                  ; finished
      LDMIA    r12!, {R3, R11}
      AND      R11, R11, #3
      CMP      R3, #32*1024*1024
      LDRHS    R3, =DuffEntry
      MOVHS    R11, #3
      BL       BangCamUpdate
      B        %BT01
02
      Pull    "R0-R4, R6, R9, lr"
      ExitSWIHandler

      LNK      Arthur2
