diff --git a/VersionASM b/VersionASM
index 7dd1a283275e20fa14d8ff82a443fa7868a5b00a..ab802e478b693c124f68a800eb9303330817a06b 100644
--- a/VersionASM
+++ b/VersionASM
@@ -13,11 +13,11 @@
                         GBLS    Module_ComponentPath
 Module_MajorVersion     SETS    "5.35"
 Module_Version          SETA    535
-Module_MinorVersion     SETS    "4.79.2.286"
-Module_Date             SETS    "01 Sep 2015"
-Module_ApplicationDate  SETS    "01-Sep-15"
+Module_MinorVersion     SETS    "4.79.2.287"
+Module_Date             SETS    "06 Sep 2015"
+Module_ApplicationDate  SETS    "06-Sep-15"
 Module_ComponentName    SETS    "Kernel"
 Module_ComponentPath    SETS    "castle/RiscOS/Sources/Kernel"
-Module_FullVersion      SETS    "5.35 (4.79.2.286)"
-Module_HelpVersion      SETS    "5.35 (01 Sep 2015) 4.79.2.286"
+Module_FullVersion      SETS    "5.35 (4.79.2.287)"
+Module_HelpVersion      SETS    "5.35 (06 Sep 2015) 4.79.2.287"
                         END
diff --git a/VersionNum b/VersionNum
index a0eac04cd33730ef18a9f7225f74c20a2775c8ba..d2e0a0e62f5d2e566bb8eb7f7dec63c2d9bfd04d 100644
--- a/VersionNum
+++ b/VersionNum
@@ -5,19 +5,19 @@
  *
  */
 #define Module_MajorVersion_CMHG        5.35
-#define Module_MinorVersion_CMHG        4.79.2.286
-#define Module_Date_CMHG                01 Sep 2015
+#define Module_MinorVersion_CMHG        4.79.2.287
+#define Module_Date_CMHG                06 Sep 2015
 
 #define Module_MajorVersion             "5.35"
 #define Module_Version                  535
-#define Module_MinorVersion             "4.79.2.286"
-#define Module_Date                     "01 Sep 2015"
+#define Module_MinorVersion             "4.79.2.287"
+#define Module_Date                     "06 Sep 2015"
 
-#define Module_ApplicationDate          "01-Sep-15"
+#define Module_ApplicationDate          "06-Sep-15"
 
 #define Module_ComponentName            "Kernel"
 #define Module_ComponentPath            "castle/RiscOS/Sources/Kernel"
 
-#define Module_FullVersion              "5.35 (4.79.2.286)"
-#define Module_HelpVersion              "5.35 (01 Sep 2015) 4.79.2.286"
+#define Module_FullVersion              "5.35 (4.79.2.287)"
+#define Module_HelpVersion              "5.35 (06 Sep 2015) 4.79.2.287"
 #define Module_LibraryVersionInfo       "5:35"
diff --git a/s/AMBControl/Workspace b/s/AMBControl/Workspace
index 79e2d3d6433b3ae29f3f76538c3528d43d76427f..91efdb5b91dd87c435c321573d99afcb9d8d3fb0 100644
--- a/s/AMBControl/Workspace
+++ b/s/AMBControl/Workspace
@@ -44,6 +44,9 @@ AMBMappedInNode     #  4               ;node ptr of mapped-in task, or 0 for non
 AMBNodeHandles      #  4               ;ptr to node handle array (1 word per entry)
 AMBPhysBin          #  4               ;ptr to physical page bin array
 AMBPhysBinEntries   #  4               ;no. of entries in physical page bin array
+  [ AMB_LazyMapIn
+AMBPageFlags        #  4               ;L2PT page flags to use when (lazily) mapping in pages
+  ]
 AMBAnchorNode       #  AMBNode_HdrSize ;dummy node - see note (1) below
 ;
   [ ChocolateAMB
diff --git a/s/AMBControl/allocate b/s/AMBControl/allocate
index f04d1823cbcb29adb541a52ad1e9f16fa6712d09..44e55c0af346bfd59c8c79933e8a1a48c539791f 100644
--- a/s/AMBControl/allocate
+++ b/s/AMBControl/allocate
@@ -65,9 +65,10 @@ allocate
         MOV     R4,#ApplicationStart
         STR     R4,[R2,#AMBNode_startaddr]
         LDR     R4,=ZeroPage+AppSpaceDANode
-        LDR     R4,[R4,#DANode_Flags]
-        AND     R4,R4,#&7F
-        STR     R4,[R2,#AMBNode_PPL]       ;PPL from bottom 8 bits of DA flags
+        LDR     R4,[R4,#DANode_Flags]      ;Get the page flags from the DA.
+        LDR     LR,=DynAreaFlags_AccessMask;Note that this is rather academic
+        AND     R4,R4,LR                   ;because various bits of code ignore
+        STR     R4,[R2,#AMBNode_PPL]       ;or overwrite these flags!
 
 ;do the actual MMU page allocation (grow from 0), for R1 pages, using node R2
         BL      growpages
diff --git a/s/AMBControl/growp b/s/AMBControl/growp
index aa56fb2e93bf862931e00922a6808c54b3d6662a..067ed8e225c4c3494b450d43a6772e1fc30b2b6b 100644
--- a/s/AMBControl/growp
+++ b/s/AMBControl/growp
@@ -55,17 +55,19 @@ growpages ROUT
         LDR     R5,[R1,#DANode_PMP]
         LDR     LR,[R1,#DANode_PMPSize]
         ADD     R5,R5,LR,LSL #2            ;current end of FreePool
-        SUB     R5,R5,R3,LSL #2            ;R5 := first required page in PMP
 
         ;R3 = no. of pages, R4 -> buffer for page entries,
-        ;R5 := start page in PMP
+        ;R5 -> end of free pool PMP
+        ; Reverse the order as we copy in order to match OS_ChangeDynamicArea
+        ; (helps ensure pages are physically contiguous - to help with any
+        ; potential DMA)
         MOV     R2,R3
         MOV     R6,#-1
 10
-        LDR     LR,[R5]
+        LDR     LR,[R5,#-4]!
         SUBS    R2,R2,#1
         STR     LR,[R4],#4
-        STR     R6,[R5],#4
+        STR     R6,[R5]
         BNE     %BT10
         SUB     R4,R4,R3,LSL #2
         LDR     R2,=ZeroPage+AppSpaceDANode ;R2 := dest for pages
diff --git a/s/AMBControl/main b/s/AMBControl/main
index 30ff2e77c8101fc278b1346e89a0f402a9bf6a1a..e363f5190a1e4ed85c5bcc729e9f1ff6a6f7089e 100644
--- a/s/AMBControl/main
+++ b/s/AMBControl/main
@@ -98,6 +98,22 @@ AMBControl_Init
         TST     R0,#CPUFlag_AbortRestartBroken ;but wait! can't use for bugged chips (eg. pre rev T StrongARM)
         MOVNE   R1,#AMBFlag_LazyMapIn_disable
         STR     R1,AMBFlags
+
+; Calculate default page flags
+        LDR     r2,=ZeroPage
+    [ MEMM_Type = "VMSAv6"
+        ADRL    r1,PPLTrans
+    |
+        LDR     r1, [r2, #ProcessorFlags]
+        TST     r1, #CPUFlag_ExtendedPages
+        ADREQL  r1, PPLTrans
+        ADRNEL  r1, PPLTransX
+    ]
+        LDR     r3,[r1]                     ; Page type + access flags
+        LDR     r2,[r2,#MMU_PCBTrans]
+        LDRB    r2,[r2]                     ; Cacheability + other attribs
+        ORR     r3,r3,r2
+        STR     r3,AMBPageFlags
   |
         MOV     R1,#AMBFlag_LazyMapIn_disable
         STR     R1,AMBFlags
diff --git a/s/AMBControl/memmap b/s/AMBControl/memmap
index cd413bdb3edf8e337b361d9fea516a2f6452c937..55b661889507b747733ade69506b56977d8f86cd 100644
--- a/s/AMBControl/memmap
+++ b/s/AMBControl/memmap
@@ -108,15 +108,8 @@ AMB_LazyFixUp ROUT
         ADD     r1,r1,r0,LSL #2                          ;r1 -> page involved, in node page list
         LDR     r2,AMBPhysBin
 
-; Calculate the L2PT protection bits in a nice way that won't produce broken code if we change MMU model
-; This should match the AP_Full entry from the PPLTrans table that gets used by BangCam (plus C+B bits)
-   [ MEMM_Type = "VMSAv6"
-        MOV     r3,#(AP_Full*L2X_APMult)+L2_ExtPage+L2_C+L2_B
-   |
-        ASSERT  (AP_Full*L2_APMult)+L2_SmallPage+L2_C+L2_B = &FFE
-        MOV     r3,#&FF0
-        ORR     r3,r3,#&E
-   ]
+; Get the correct default page flags
+        LDR     r3,AMBPageFlags
         LDR     r4,[r1]
         MOV     r6,r4
         PageNumToL2PT r4,r2,r3,r5
diff --git a/s/AMBControl/shrinkp b/s/AMBControl/shrinkp
index e53bb7595a75d54c3c987c8464d0d1a059f92339..a5816c95c879ed3427b49570262ec3547742bdd9 100644
--- a/s/AMBControl/shrinkp
+++ b/s/AMBControl/shrinkp
@@ -85,9 +85,13 @@ shrinkpages
         LDR     R7,=ZeroPage
         LDR     R7,[R7,#CamEntriesPointer]
         ADD     R7,R7,#CAM_PMP
+        ADD     R4,R4,R3,LSL #2
         MOV     R0,R3
+        ; Reverse the page order as we copy, to match the reverse we performed
+        ; on grow (try and keep free pool pages in optimal order for future
+        ; grows)
 10
-        LDR     LR,[R4],#4
+        LDR     LR,[R4,#-4]!
         SUBS    R0,R0,#1
         STR     LR,[R5,R6,LSL #2]          ;add to free pool PMP
         ADD     LR,R7,LR,LSL #CAM_EntrySizeLog2
diff --git a/s/ChangeDyn b/s/ChangeDyn
index 0e052e7c7807123e874338672e033ef8301b14c7..40997138da809c3afea0b9a9f298a49ba41a12b3 100644
--- a/s/ChangeDyn
+++ b/s/ChangeDyn
@@ -2518,7 +2518,7 @@ DynArea_Locate Entry "r2-r5"
         B       %BT50
 60
         LDR     r1, [r10, #DANode_Flags]
-        LDR     r0, [r10, #DANode_Size]
+        LDR     r0, [r10, #DANode_MaxSize]
         TST     r1, #DynAreaFlags_DoublyMapped
         LDR     r1, [r10, #DANode_Base]
         ADD     r2, r1, r0                      ; r1:=base r2:=top
@@ -2539,11 +2539,11 @@ DynArea_Locate Entry "r2-r5"
 ;
 ; in:   r0 = reason code (21)
 ;       r1 = area number
-;       r2 = pointer to array of (PMP page index, phys page index, PMP page flag) tuples
+;       r2 = pointer to array of (PMP page index, phys page index, page flag) tuples
 ;            phys page index -1 to release
 ;            phys page index -2 to let kernel pick page
 ;            otherwise page number to use
-;            PMP page flags are defined by DynAreaFlags_PMPPhysOpAccessMask
+;            page flags are defined by DynAreaFlags_PMPPhysOpAccessMask
 ;       r3 = number of entries
 ;
 ; out:  r0-r1 preserved (error if not all of region successfully updated)
@@ -3528,17 +3528,13 @@ DynArea_PMP_GetInfo ROUT
 ;
 ;       Internal routine called by DynamicAreaSWI
 ;
-;       Although designed for use with PMPs, this call works with regular DAs
-;       too (just returns zero for r6 & r7)
-;
 ; in:   r0 = reason code (25)
 ;       r1 = area number
 ;       r2 = pointer to input/output array:
-;            +0: PMP page index (filled in on entry)
-;            +4: phys page index (filled in on exit, -1 if none)
-;            +8: PMP page flags (filled in on exit, 0 if none)
-;            +12: DA page index (filled in on exit, -1 if not mapped)
-;            +16: page flags (filled in on exit, 0 if not mapped)
+;            +0: PMP page index
+;            +4: phys page number
+;            +8: DA page index
+;            +12: page flags
 ;       r3 = number of entries
 ;
 ; out:  r0-r3 preserved
@@ -3546,6 +3542,16 @@ DynArea_PMP_GetInfo ROUT
 ;       All other registers preserved
 ;       Array updated with page details
 ;
+; On entry, for each array entry either the PMP page index, phys page number, or
+; DA page index must be provided, with the other indices set to -1 (page flags
+; are ignored).
+;
+; On exit, if the page is a member of the PMP, the entries will be filled in as
+; appropriate. If the page isn't mapped in (and it was a lookup by PMP page
+; index/phys page number) the DA page index will be set to -1. If no physical
+; page is allocated (or the page isn't a member of the PMP) the page flags will
+; be set to 0.
+;
 
 DynArea_PMP_GetPages ROUT
         Entry   "r0-r9"
@@ -3556,52 +3562,125 @@ DynArea_PMP_GetPages ROUT
   ]
         BCC     %FT90                   ; [it doesn't]
         ; r10 -> DANode
-        LDR     r8, [r10, #DANode_PMP]
-        CMP     r8, #0
+        LDR     r6, [r10, #DANode_PMP]
+        CMP     r6, #0
         BEQ     %FT90
         LDR     r9, [r10, #DANode_PMPMaxSize]
         BEQ     %FT90
-        LDR     r7, =ZeroPage
+        LDR     r11, =ZeroPage
         LDR     r5, [r10, #DANode_Base]
-        LDR     r7, [r7, #CamEntriesPointer]
-        LDR     r11, =DynAreaFlags_PMPLogOpAccessMask
-        LDR     r12, =Nowhere
+        LDR     r7, [r11, #MaxCamEntry]
+        LDR     r8, =L2PT
+        LDR     r11, [r11, #CamEntriesPointer]
+        LDR     r12, =DynAreaFlags_PMPLogOpAccessMask
         ; Usage in main loop:
         ; r2 -> input page list
         ; r3 = length
-        ; r4 = current entry PMP index
         ; r5 -> DA base
-        ; r7 -> CAM
-        ; r8 -> PMP
+        ; r6 -> PMP
+        ; r7 = MaxCamEntry
+        ; r8 -> L2PT
         ; r9 = PMP size
         ; r10 -> DANode
-        ; r11 = PMPLogOpAccessMask
-        ; r12 = Nowhere
-        ; r0, r1, r5, r6 temp
+        ; r11 -> CAM
+        ; r12 = DynAreaFlags_PMPLogOpAccessMask
+        ; r0, r1, r4 temp
 10
         SUBS    r3, r3, #1
         BLT     %FT80
-        LDR     r4, [r2], #4
+        ; Get the entry
+        LDMIA   r2, {r0, r1, r4}
+        ; PMP page provided?
+        CMP     r0, #-1
+        BNE     %FT50
+        ; Phys page provided?
+        CMP     r1, #-1
+        BNE     %FT20
+        ; DA page provided
+        ; n.b. skipping any range check here since it won't hurt if the page
+        ; doesn't belong to us
+        Push    "r3, r5, r9-r11"
+        ADD     r4, r5, r4, LSL #12
+        BL      logical_to_physical
+        BLCC    physical_to_ppn
+        MOV     r0, r3
+        Pull    "r3, r5, r9-r11"
+        BCS     %FT15
+        ; r0 = PPN, check to see if it belongs to us
+        ADD     r1, r11, r0, LSL #CAM_EntrySizeLog2
+        ASSERT  CAM_PageFlags=4
+        ASSERT  CAM_PMP=8
+        ASSERT  CAM_PMPIndex=12
+        LDMIB   r1, {r1, r4, lr}
+        TST     r1, #DynAreaFlags_PMP
+        BEQ     %FT15
+        CMP     r4, r10
+        BNE     %FT15
+        STR     lr, [r2], #4            ; Store PMP page index
+        AND     r1, r1, r12
+        STR     r0, [r2], #8            ; Store phys page number, skip DA page index
+        STR     r1, [r2], #4            ; Store page flags
+        B       %BT10
+15
+        ; Bad DA page index
+        ; PMP page index & phys page number are already known to be -1, so just
+        ; store flags
+        MOV     r0, #0
+        STR     r0, [r2, #12]
+        ADD     r2, r2, #16
+        B       %BT10
+
+20
+        ; Check for silly phys page number
+        CMP     r1, r7
+        BHI     %FT91
+        ADD     r1, r11, r1, LSL #CAM_EntrySizeLog2
+        ASSERT  CAM_LogAddr=0
+        ASSERT  CAM_PageFlags=4
+        ASSERT  CAM_PMP=8
+        ASSERT  CAM_PMPIndex=12
+        LDMIA   r1, {r0, r1, r4, lr}
+        TST     r1, #DynAreaFlags_PMP
+        BEQ     %FT25
+        CMP     r4, r10
+        BNE     %FT25
+        LDR     r4, =Nowhere
+        STR     lr, [r2], #8            ; Store PMP page index, skip phys page number
+        TEQ     r0, r4
+        B       %FT55
+
+25
+        ; Bad phys page number
+        ; PMP page index known to be -1, so store DA page index + flags
+        ADD     r2, r2, #8
+        MOV     r0, #-1
+        MOV     r1, #0
+        STMIA   r2!, {r0-r1}
+        B       %BT10
+
+50
         ; Check for silly PMP page index
-        CMP     r4, r9
+        CMP     r0, r9
         BHS     %FT91
         ; Look up the page that's currently in the PMP
-        LDR     r0, [r8, r4, LSL #2]
-        MOV     r1, #0                  ; Currently, no flags
-        STMIA   r2!, {r0-r1}
+        LDR     r0, [r6, r0, LSL #2]
+        ADD     r2, r2, #4
+        STR     r0, [r2], #4            ; Store phys page number
         ; Does the page exist?
         CMP     r0, #-1
-        ADDNE   r0, r7, r0, LSL #CAM_EntrySizeLog2
+        LDR     r4, =Nowhere
+        ADDNE   r0, r11, r0, LSL #CAM_EntrySizeLog2
+        MOVEQ   r1, #0                  ; No physical page, so no flags
         ASSERT  CAM_LogAddr=0
         ASSERT  CAM_PageFlags=4
         LDMNEIA r0, {r0-r1}             ; Get log addr, flags from CAM
-        TEQNE   r0, r12
+        TEQNE   r0, r4
+55
         MOVEQ   r0, #-1                 ; No physical page, or not mapped
         SUBNE   r0, r0, r5
-        MOVEQ   r1, #0
         MOVNE   r0, r0, LSR #12
-        AND     r1, r1, r11             ; Mask returned flags
-        STMIA   r2!, {r0-r1}
+        AND     r1, r1, r12             ; Mask returned flags
+        STMIA   r2!, {r0-r1}            ; Store DA page index, flags
         B       %BT10
 
 80
@@ -3619,6 +3698,7 @@ DynArea_PMP_GetPages ROUT
  |
         SETV
  ]
+        FRAMSTR r0
         EXIT
 
 
@@ -4447,11 +4527,9 @@ DynArea_AddrLookup_loop
       ]
 
 ; Now that the system heap is initialised we can create a page list for the
-; free pool and start pushing the free pages into it. We want the pages at the
-; start of the list to be the VRAM block, followed by all the other pages in
-; order of increasing speed. However it's highly unlikely that we'll be able to
-; build the full page list without having to grow the system heap - for which
-; we'd want the fast pages to be available.
+; free pool and start pushing the free pages into it. However it's highly
+; unlikely that we'll be able to build the full page list without having to
+; grow the system heap - for which we'd want the fast pages to be available.
 
 ; So to cope with this we start by putting the fast pages into the page list,
 ; growing the system heap for every page we insert (a bit slow but reliable).
@@ -4459,8 +4537,6 @@ DynArea_AddrLookup_loop
 ; on success we then switch to a different algorithm which fills the main page
 ; list.
 
-; TODO - Make sure pages are inserted in an order which keeps them in consecutive physical order when being transferred to a DA (and make sure shrink/grow preserves order!)
-
         SUB     sp, sp, #4                      ; Store the initial list on the stack
         LDR     r5, =ZeroPage
         LDR     r6, =ZeroPage+FreePoolDANode
@@ -4482,14 +4558,12 @@ DynArea_AddrLookup_loop
         ADD     r9, r5, #PhysRamTable
         LDMIA   r9!, {r0, r10}                  ; get VRAM info
         MOV     r10, r10, LSR #12               ; r10 = current page number
+        LDMIA   r9!, {r0, r11}                  ; get first regular RAM chunk
+        SUB     r10, r10, #1                    ; set things up so the first call to NextFreePage will return the first page of the block
+        MOV     r11, r11, LSR #12
+        ADD     r11, r11, #1
         LDR     r4, [r5, #CamEntriesPointer]
 10
-        LDMIA   r9!, {r0, r11}                  ; get next block
-        MOVS    r11, r11, LSR #12               ; if no more blocks left...
-        MOVEQ   r10, #0
-        ADDEQ   r9, r5, #PhysRamTable           ; ...panic and use the VRAM
-        BEQ     %BT10
-15
         ; See if we have enough space
         LDR     r3, [r5, #MaxCamEntry]
         ADD     r3, r3, #1
@@ -4498,22 +4572,14 @@ DynArea_AddrLookup_loop
         MOV     r0, #HeapReason_Desc
         BL      DoSysHeapOpWithExtension        ; HACK - check space before calling, to avoid crashing when the grow fails and tries to generate an error (vector table not initialised yet, so crashes when UKSWIV is invoked in order to call MessageTrans)
         Pull    "r3"
-        SUB     r2, r2, #4096 ; Paranoia
+        SUB     r2, r2, #4096                   ; Paranoia
         CMP     r2, r3
         BLT     %FT20
         BL      ClaimSysHeapNode
         BVC     %FT40
-        ; Find a page we can use to grow the system heap
 20
-        CMP     r10, r7
-        CMPHS   r8, r10
-        BHS     %FT30                           ; page is in statics
-        ; Check the CAM map to see if the page is already taken - this will detect the DMA regions, which aren't included in InitUsedStart/InitUsedEnd
-        ADD     r0, r4, r10, LSL #CAM_EntrySizeLog2
-        LDR     lr, [r0, #CAM_PageFlags]
-        TST     lr, #PageFlags_Unavailable
-        BNE     %FT30
-        ; Found a page, add it to the free pool
+        ; Find a page we can use to grow the system heap
+        BL      NextFreePage                    ; n.b. no out-of-pages check
         STR     r10, [sp]
         LDR     lr, =AP_FreePool :AND: DynAreaFlags_AccessMask
         STR     lr, [r0, #CAM_PageFlags]
@@ -4526,12 +4592,8 @@ DynArea_AddrLookup_loop
         MOV     r0, #ChangeDyn_SysHeap
         MOV     r1, #4096
         SWI     XOS_ChangeDynamicArea
-30
-        ; Move on to next page
-        SUBS    r11, r11, #1
-        ADD     r10, r10, #1
-        BEQ     %BT10
-        B       %BT15
+        B       %BT10
+
 40
         ; We've successfully allocated the memory for the PMP - start filling
         ; it in. To ensure the pages are in the correct order we need to fill
@@ -4548,34 +4610,10 @@ DynArea_AddrLookup_loop
         MOV     r1, r3
         STR     r2, [r6, #DANode_PMP]
 45
-        CMP     r10, r7
-        CMPHS   r8, r10
-        BHS     %FT50                           ; page is in statics
-        ; Check the CAM map to see if the page is already taken - this will detect the DMA regions, which aren't included in InitUsedStart/InitUsedEnd
-        ADD     r0, r4, r10, LSL #CAM_EntrySizeLog2
-        LDR     lr, [r0, #CAM_PageFlags]
-        TST     lr, #PageFlags_Unavailable
-        BNE     %FT50
-        ; Found a page, add it to the free pool
-        STR     r10, [r3, #-4]!
-50
-        ; Move on to next page
-        SUBS    r11, r11, #1
-        ADD     r10, r10, #1
-        BNE     %BT45
-        ADD     lr, r5, #PhysRamTable+8
-        CMP     lr, r9                          ; if we've just processed the VRAM chunk, we're done
-        BEQ     %FT55
-        LDMIA   r9!, {r0, r11}                  ; else get next block
-        MOVS    r11, r11, LSR #12               ; if no more blocks left...
-        BNE     %BT45
-        MOV     r10, #0
-        MOV     r9, lr
-        LDMDB   r9, {r0, r11}                   ; ...then process VRAM
-        MOVS    r11, r11, LSR #12               ; And if no VRAM...
-        BNE     %BT45
-                                                ; ...then we're done
-55
+        BL      NextFreePage
+        CMP     r10, #-1
+        STRNE   r10, [r3, #-4]!
+        BNE     %BT45                           ; Keep going until we run out of pages
         ; Left with:
         ; r1 -> end of memory block
         ; r2 -> start of memory block
@@ -4621,6 +4659,83 @@ DynArea_AddrLookup_loop
       ]
         EXIT
 
+;
+; NextFreePage - Find next page to insert into the free pool on startup
+;
+; In:
+;   r4 -> CAM
+;   r7 = page number of start of static chunk
+;   r8 = page number of end of static chunk
+;   r9 -> next PhysRamTable entry
+;   r10 = Current page number
+;   r11 = Number of pages left in current chunk
+; Out:
+;   r0 -> CAM entry for page
+;   r10 = Next free page in optimal order, -1 if no more pages
+;   r9, r11 updated
+;
+; We have to move all free pages (ie ones not occupied by the static pages)
+; into the free pool.
+; By default, pages will get taken from the end of the free pool when other
+; dynamic areas are initialised or grown. So make sure that the slowest RAM
+; is at the start of the free pool and the fastest is at the end; this is the
+; reverse of the order in PhysRamTable. Also, within each group of pages (i.e.
+; PhysRamTable entry), we want the pages to be in decreasing physical address
+; order - so that when they are moved to a DA they end up in increasing address
+; order, leading to more optimal DMA transfer lists.
+;
+; Also note that the VRAM block is kept at the start of the free pool, mainly
+; to match old behaviour (it's not clear whether moving it elsewhere will have
+; any significant impact on the system - especially when you consider that
+; shrinking screen memory will end up adding the pages to the end of the pool
+; rather than the start).
+;
+; Over time this optimal ordering will be lost, so at a later date it might be
+; nice to re-sort pages as they are added back into the free pool (and move the
+; VRAM block to the end of PhysRamTable, so that it's in order fast RAM -> slow
+; RAM -> fast DMA -> slow DMA -> VRAM, so that sorting by page number is
+; all that's required to deal with both contiguity and desirability)
+;
+; In terms of this routine, we fill the free pool from the highest entry down,
+; so we want the first page returned to be the lowest-numbered page from the
+; first (non-VRAM) PhysRamTable entry.
+;
+NextFreePage    ROUT
+        Entry
+10
+        SUBS    r11, r11, #1
+        ADD     r10, r10, #1
+        BEQ     %FT30
+20
+        CMP     r10, r7
+        CMPHS   r8, r10
+        BHS     %BT10                           ; page is in statics
+        ; Check the CAM map to see if the page is already taken - this will detect the DMA regions, which aren't included in InitUsedStart/InitUsedEnd
+        ADD     r0, r4, r10, LSL #CAM_EntrySizeLog2
+        LDR     lr, [r0, #CAM_PageFlags]
+        TST     lr, #PageFlags_Unavailable
+        BNE     %BT10
+        ; Page is good
+        EXIT
+
+30
+        ; Advance to next block
+        LDR     lr, =ZeroPage+PhysRamTable+8
+        CMP     lr, r9                          ; if we've just processed the VRAM block, we're done
+        BEQ     %FT90
+        LDMIA   r9!, {r0, r11}                  ; else get next block
+        MOVS    r11, r11, LSR #12               ; if no more blocks left...
+        BNE     %BT20
+        MOV     r10, #0
+        MOV     r9, lr
+        LDMDB   lr, {r0, r11}                   ; ...then process VRAM
+        MOVS    r11, r11, LSR #12               ; And if no VRAM...
+        BNE     %BT20
+90                                              ; ...then we're done
+        MOV     r10, #-1
+        EXIT
+
+
         LTORG
 
 InitFreePoolTable
@@ -6009,7 +6124,7 @@ DoTheGrowPagesSpecified ROUT
         LDR     r6, [r8, #4]                    ;logical address of src page
 
         ; If the required page is in the free pool, we don't need to preserve its contents
-        ; TODO - have 'volatile' PMP page flag which can be used to indicate that pages can just be taken? (so will work with any PMP)
+        ; TODO - have 'volatile' page flag which can be used to indicate that pages can just be taken? (so will work with any PMP)
         TST     r2, #DynAreaFlags_PMP
         BEQ     %FT73
         CMP     r3, r11