; 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.
;
; > MemInfo

        LTORG

;----------------------------------------------------------------------------------------
; MemorySWI
;
;       In:     r0 = reason code and flags
;                       bits 0-7  = reason code
;                       bits 3-31 = reason specific flags
;       Out:    specific to reason codes
;
;       Perform miscellaneous operations for memory management.
;
MemorySWI       ROUT
        Push    lr                              ; Save real return address.
        AND     lr, r0, #&FF                    ; Get reason code.
        CMP     lr, #(%40-%30):SHR:2            ; If valid reason code then
        ADDCC   lr, lr, #(%30-%10):SHR:2        ;   determine where to jump to in branch table,
        ADDCC   lr, pc, lr, LSL #2
        Push    lr, CC                          ;   save address so we can
10
        ADRCC   lr, MemReturn                   ;   set up default return address for handler routines
        Pull    pc, CC                          ;   and jump into branch table.
20
        ADRL    r0, ErrorBlock_HeapBadReason    ; Otherwise, unknown reason code.
        SETV
        ; Drop through to...

MemReturn
 [ International
        BLVS    TranslateError
 ]
        Pull    lr                              ; Get back real return address.
        BVS     SLVK_SetV
        ExitSWIHandler

30
        B       MemoryConvert                   ; 0
        B       %BT20                           ; Reason codes 1-5 are reserved.
        B       %BT20
        B       %BT20
        B       %BT20
        B       %BT20
        B       MemoryPhysSize                  ; 6
        B       MemoryReadPhys                  ; 7
        B       MemoryAmounts                   ; 8
        B       MemoryIOSpace                   ; 9
        B       MemoryFreePoolLock              ; 10
        B       %BT20                           ; Reason code 11 reserved (for PCImapping).
        B       RecommendPage                   ; 12
        B       MapIOpermanent                  ; 13
        B       AccessPhysAddr                  ; 14
        B       ReleasePhysAddr                 ; 15
        B       MemoryAreaInfo                  ; 16
40


;----------------------------------------------------------------------------------------
; MemoryConvert
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     0 (reason code)
;                       8       page number provided when set
;                       9       logical address provided when set
;                       10      physical address provided when set
;                       11      fill in page number when set
;                       12      fill in logical address when set
;                       13      fill in physical address when set
;                       14-15   0,1=don't change cacheability
;                               2=disable caching on these pages
;                               3=enable caching on these pages
;                       16-31   reserved (set to 0)
;               r1 -> page block
;               r2 = number of 3 word entries in page block
;
;       Out:    r1 -> updated page block
;
;       Converts between representations of memory addresses. Can also set the
;       cacheability of the specified pages.
;

; Declare symbols used for decoding flags (given and wanted are used
; so that C can be cleared by rotates of the form a,b). We have to munge
; the flags a bit to make the rotates even.
;
ppn             *       1:SHL:0         ; Bits for address formats.
logical         *       1:SHL:1
physical        *       1:SHL:2
all             *       ppn :OR: logical :OR: physical
given           *       24              ; Rotate for given fields.
wanted          *       20              ; Rotate for wanted fields.
ppn_bits        *       ((ppn :SHL: 4) :OR: ppn)
logical_bits    *       ((logical :SHL: 4) :OR: logical)
physical_bits   *       ((physical :SHL: 4) :OR: physical)
cacheable_bit   *       1:SHL:15
alter_cacheable *       1:SHL:16
flush_tlb       *       1:SHL:30        ; Internal flag
flush_cache     *       1:SHL:31        ; Internal flag.
flush_all       *       flush_tlb :OR: flush_cache

MemoryConvert   ROUT
        Entry   "r0-r11"                ; Need lots of registers!!

;        MRS     lr, CPSR
;        Push    "lr"
;        ORR     lr, lr, #I32_bit+F32_bit
;        MSR     CPSR_c, lr

        BIC     lr, r0, #all,given      ; Need to munge r0 to get rotates to work (must be even).
        AND     r0, r0, #all,given
        ORR     r0, r0, lr, LSL #1      ; Move bits 11-30 to 12-31.

        TST     r0, #all,given          ; Check for invalid argument (no fields provided)
        TEQNE   r2, #0                  ;   (no entries in table).
        ADREQL  r0, ErrorBlock_BadParameters
        BEQ     %FT95

        EOR     lr, r0, r0, LSL #given-wanted   ; If flag bits 8-10 and 12-14 contain common bits then
        AND     lr, lr, #all,wanted             ;   clear bits in 12-14 (ie. don't fill in fields already given).
        EOR     lr, lr, #all,wanted
        BIC     r0, r0, lr

        LDR     r6, =ZeroPage
        LDR     r7, [r6, #MaxCamEntry]
        LDR     r6, [r6, #CamEntriesPointer]
        LDR     r8, =L2PT

        BIC     r0, r0, #flush_all      ; Bit set if any page is really made uncacheable (we must flush the cache).
10
        SUBS    r2, r2, #1
        BCC     %FT70

        LDMIA   r1!, {r3-r5}            ; Get next three word entry (PN,LA,PA) and move on pointer.

   [ ChocolateAMB
        BL      handle_AMBHonesty       ; may need to make page honest (as if not lazily mapped)
   ]

        TST     r0, #physical,wanted    ; If PA not wanted
        BEQ     %FT20                   ;   then skip.
        TST     r0, #logical,given      ; If LA not given (rotate clears C) then
        BLEQ    ppn_to_logical          ;   get it from PN (PA wanted (not given) & LA not given => PN given).
        BLCC    logical_to_physical     ; Get PA from LA.
        BCS     %FT80
        TST     r0, #logical,wanted
        STRNE   r4, [r1, #-8]           ; Store back LA if wanted.
        STR     r5, [r1, #-4]           ; Store back PA.
20
        TST     r0, #alter_cacheable    ; If altering cacheability
        EORNE   lr, r0, #ppn,given      ;   and PN not given
        TSTNE   lr, #ppn,given
        TSTEQ   r0, #ppn,wanted         ;   OR PN wanted then don't skip
        BEQ     %FT30                   ; else skip.
        TST     r0, #physical_bits,given        ; If PA not given and PA not wanted (rotate clears C) then
        BLEQ    logical_to_physical             ;   get it from LA (PN wanted/not given & PA not given => LA given).
        BLCC    physical_to_ppn         ; Get PN from PA.
        BCS     %FT80
        TST     r0, #ppn,wanted
        STRNE   r3, [r1, #-12]          ; Store back PN if wanted.
30
        TST     r0, #logical,wanted     ; If LA wanted
        EORNE   lr, r0, #physical,wanted
        TSTNE   lr, #physical,wanted    ;   and PA not wanted then don't skip
        BEQ     %FT40                   ; else skip.
        TST     r0, #alter_cacheable    ; If not changing cacheability (already have PN)
        TSTEQ   r0, #ppn_bits,given     ;   and PN not given and PN not wanted (rotate clears C) then
        BLEQ    physical_to_ppn         ;   get it from PA (LA wanted (not given) & PN not given => PA given).
        BLCC    ppn_to_logical          ; Get LA from PN.
        BCS     %FT80
        STR     r4, [r1, #-8]           ; Store back LA.
40
        TST     r0, #alter_cacheable
        BEQ     %BT10

        CMP     r7, r3                  ; Make sure page number is valid (might not have done any conversion).
        BCC     %FT80

        ADD     r3, r6, r3, LSL #3      ; Point to CAM entry for this page.
        LDMIA   r3, {r4,r5}             ; Get logical address and PPL.

        AND     lr, r5, #PageFlags_TempUncacheableBits
        TST     r0, #cacheable_bit
        BNE     %FT50

        TEQ     lr, #PageFlags_TempUncacheableBits      ; Make uncacheable (increment count).
        BEQ     %BT10                                   ; If count has reached max then go no further (should not happen).
        TEQ     lr, #0                                  ; EQ => we have to change L2.
        ADD     r5, r5, #1:SHL:TempUncacheableShift
        B       %FT60
50
        TEQ     lr, #0                                  ; Make cacheable (decrement count).
        BEQ     %BT10                                   ; If count is already 0 then go no further (page already cacheable).
        SUB     r5, r5, #1:SHL:TempUncacheableShift
        TST     r5, #PageFlags_TempUncacheableBits      ; EQ => we have to change L2.
60
        STR     r5, [r3, #4]            ; Write back new PPL.
        BNE     %BT10                   ; Do next entry if we don't have to change L2.

        MOV     r4, r4, LSR #12
        ADD     r4, r8, r4, LSL #2      ; Address of L2 entry for logical address.
        LDR     r5, [r4]                ; Get L2 entry (safe as we know address is valid).
        ORR     r0, r0, #flush_tlb
        TST     r0, #cacheable_bit
        BICEQ   r5, r5, #L2_C           ; Disable/enable cacheability.
        ORREQ   r0, r0, #flush_cache    ; If disable then we must flush cache later.
        ORRNE   r5, r5, #L2_C
        STR     r5, [r4]                ; Write back new L2 entry.

        B       %BT10                   ; Do next entry.

70
        ; *** KJB - this assumes that uncacheable pages still allow cache hits (true on all
        ; ARMs so far).
        ; *** Also, fix it up to do it page by page.
        TST     r0, #flush_tlb
        EXIT    EQ                      ; If not flush_tlb, can't have flush_cache
        MOV     r1, r0
        LDR     r0, =ZeroPage
        ARMop   TLB_InvalidateAll,,,r0
        TST     r1, #flush_cache        ; If any page has been made uncacheable in L2 then flush!
        EXIT    EQ
        LDR     r0, =ZeroPage
        ARMop   Cache_CleanInvalidateAll,,,r0
;75
;        Pull    "lr"
;        MSR     CPSR_c, lr
        EXIT

80
        TST     r0, #alter_cacheable    ; If we haven't changed any cacheability stuff then
        BEQ     %FT90                   ;   just return error.

        AND     lr, r0, #all,wanted             ; Get wanted flags.
        LDMIA   sp, {r0,r1,r3}                  ; Get back original flags, pointer and count.
        ORR     r0, r0, lr, LSR #given-wanted   ; Wanted fields are now also given as we have done the conversion.
        BIC     r0, r0, #all:SHL:11             ; Clear wanted flags, we only want to change cacheability.
        EOR     r0, r0, #cacheable_bit          ; If we made them uncacheable then make them cacheable again & v.v.
        SUB     r2, r3, r2
        SUBS    r2, r2, #1              ; Change back the entries we have changed up to (but excluding) the error entry.
        BLNE    MemoryConvert
90
        ADRL    r0, ErrorBlock_BadAddress
95
        STR     r0, [sp]
        SETV
        EXIT

   [ ChocolateAMB
;
;  entry: r3,r4,r5 = provided PN,LA,PA triple for entry to make honest (at least one given)
;         r0 bits flag which of PN,LA,PA are given
;  exit:  mapping made honest (as if not lazily mapped) if necessary
handle_AMBHonesty  ROUT
        Push    "r0, r3-r5, lr"
        TST     r0, #logical,given
        BEQ     %FT10
        MOV     r0, r4
        BL      AMB_MakeHonestLA
        B       %FT90
10
        TST     r0, #ppn,given
        BEQ     %FT20
15
        MOV     r0, r3
        BL      AMB_MakeHonestPN
        B       %FT90
20
        TST     r0, #physical,given
        BEQ     %FT90
        Push    "r7, r9-r11"
        LDR     r14, =ZeroPage
        LDR     r7, [r14, #MaxCamEntry]
        BL      physical_to_ppn
        Pull    "r7, r9-r11"
        BCC     %BT15
90
        Pull    "r0, r3-r5, pc"

   ] ;ChocolateAMB


;----------------------------------------------------------------------------------------
; ppn_to_logical
;
;       In:     r3 = page number
;               r5 = physical address if given
;               r6 = CamEntriesPointer
;               r7 = MaxCamEntry
;
;       Out:    r9 corrupted
;               CC => r4 = logical address
;               CS => invalid page number
;
;       Convert physical page number to logical address.
;
ppn_to_logical
        CMP     r7, r3                  ; Validate page number.
 [ No26bitCode
        BCC     meminfo_returncs        ; Invalid so return C set.
 |
        ORRCCS  pc, lr, #C_bit          ; Invalid so return C set.
 ]

        LDR     r4, [r6, r3, LSL #3]    ; If valid then lookup logical address.
        TST     r0, #physical,given     ; If physical address was given then
        LDRNE   r9, =&FFF
        ANDNE   r9, r5, r9              ;   mask off page offset
        ORRNE   r4, r4, r9              ;   and combine with logical address.
 [ No26bitCode
        CLC
        MOV     pc, lr
 |
        BICS    pc, lr, #C_bit          ; Return C clear.
 ]


;----------------------------------------------------------------------------------------
; logical_to_physical
;
;       In:     r4 = logical address
;               r8 = L2PT
;
;       Out:    r9 corrupted
;               CC => r5 = physical address
;               CS => invalid logical address, r5 corrupted
;
;       Convert logical address to physical address.
;
logical_to_physical
        MOV     r9, r4, LSR #12         ; r9 = logical page number
        ADD     r9, r8, r9, LSL #2      ; r9 -> L2PT entry for logical address
        MOV     r5, r9, LSR #12         ; r5 = page offset to L2PT entry for logical address
        LDR     r5, [r8, r5, LSL #2]    ; r5 = L2PT entry for L2PT entry for logical address
        EOR     r5, r5, #2_10           ; Check for valid page.
        TST     r5, #3
 [ No26bitCode
        BNE     meminfo_returncs
 |
        ORRNES  pc, lr, #C_bit
 ]

        LDR     r5, [r9]                ; r5 = L2PT entry for logical address
        EOR     r5, r5, #2_10           ; Check for valid page.
        TST     r5, #3
 [ No26bitCode
        BNE     meminfo_returncs
 |
        ORRNES  pc, lr, #C_bit
 ]

        LDR     r9, =&FFF               ; Valid so
        BIC     r5, r5, r9              ;   mask off bits 0-11,
        AND     r9, r4, r9              ;   get page offset from logical page
        ORR     r5, r5, r9              ;   combine with physical page address.
 [ No26bitCode
        CLC
        MOV     pc, lr
 |
        BICS    pc, lr, #C_bit
 ]

meminfo_returncs_pullr5
        Pull    "r5"
meminfo_returncs
        SEC
        MOV     pc, lr

;----------------------------------------------------------------------------------------
; physical_to_ppn
;
;       In:     r5 = physical address
;               r7 = MaxCamEntry
;
;       Out:    r9-r11 corrupted
;               CC => r3 = page number
;               CS => invalid physical address, r3 corrupted
;
;       Convert physical address to physical page number.
;
physical_to_ppn ROUT
        Push    "r5"
        LDR     r9, =ZeroPage+PhysRamTable
        MOV     r3, #0                  ; Start at page 0.
        MOV     r5, r5, LSR #12
10
        CMP     r7, r3                  ; Stop if we run out of pages
        BCC     meminfo_returncs_pullr5

        LDMIA   r9!, {r10,r11}          ; Get start address and size of next block.
        SUB     r10, r5, r10, LSR #12   ; Determine if given address is in this block.
        CMP     r10, r11, LSR #12
        ADDCS   r3, r3, r11, LSR #12    ; Move on to next block.
        BCS     %BT10

        Pull    "r5"

        ADD     r3, r3, r10
        CLC
        MOV     pc, lr


;----------------------------------------------------------------------------------------
; Symbols used in MemoryPhysSize and MemoryReadPhys
;

; Shifts to determine number of bytes/words to allocate in table.
BitShift        *       10
ByteShift       *       BitShift + 3
WordShift       *       ByteShift + 2

; Bit patterns for different types of memory.
NotPresent      *       &00000000
DRAM_Pattern    *       &11111111
VRAM_Pattern    *       &22222222
ROM_Pattern     *       &33333333
IO_Pattern      *       &44444444
NotAvailable    *       &88888888


;----------------------------------------------------------------------------------------
; MemoryPhysSize
;
;       In:     r0 = 6 (reason code with flag bits 8-31 clear)
;
;       Out:    r1 = table size (in bytes)
;               r2 = page size (in bytes)
;
;       Returns information about the memory arrangement table.
;
MemoryPhysSize
 [ HAL
        Entry   "r0-r1,r3,sb,ip"
        AddressHAL
        MOV     r0, #PhysInfo_GetTableSize
        ADD     r1, sp, #4
        CallHAL HAL_PhysInfo
        MOV     r2, #4*1024
        EXIT
 |
        MOV     r1, #PhysSpaceSize :SHR: ByteShift
        MOV     r2, #4*1024
        MOV     pc, lr
 ]


;----------------------------------------------------------------------------------------
; MemoryReadPhys
;
;       In:     r0 = 7 (reason code with flag bits 8-31 clear)
;               r1 -> memory arrangement table to be filled in
;
;       Out:    r1 -> filled in memory arrangement table
;
;       Returns the physical memory arrangement table in the given block.
;
MemoryReadPhys  ROUT

        Entry   "r0-r12"
        AddressHAL
        MOV     r0, #PhysInfo_WriteTable
        SUB     sp, sp, #8
        MOV     r2, sp
        CallHAL HAL_PhysInfo            ; fills in everything except DRAM
        LDR     r0, [sp], #4
        LDR     r11, [sp], #4

        ; r0 to r11 is DRAM or not present.
        LDR     r1, [sp, #4]            ; Get table address back
        ADD     r1, r1, r0, LSR #ByteShift
        MOV     r2, r0                  ; Current physical address.
        MOV     r3, #0                  ; Next word to store in table.
        MOV     r4, #32                 ; How much more we have to shift r3 before storing it.
        LDR     r6, =ZeroPage+CamEntriesPointer
        LDR     r7, [r6]
        ADD     r7, r7, #4              ; Point to PPL entries.
        LDR     r8, [r6, #MaxCamEntry-CamEntriesPointer]
        MOV     r5, #0                  ; last block address processed + 1
        Push    "r5"

        ; Ugly logic to process PhysRamTable entries in address order instead of physical page order
10
        Pull    "r12"
        MVN     lr, #0
        MOV     r5, #0                  ; Current page number.
        Push    "r5,lr"
        LDR     r6, =ZeroPage+PhysRamTable
        MOV     r10, #0
11
        ADD     r5, r5, r10, LSR #12
        LDMIA   r6!, {r9,r10}           ; Get physical address and size of next block.
        CMP     r10, #0
        BEQ     %FT12

        CMP     r9, r0                  ; If not DRAM then
        CMPHS   r11, r9
        BLO     %BT11                   ; try next block.

        CMP     r9, r12                 ; have we processed this entry?
        CMPHS   lr, r9                  ; is it the lowest one we've seen?
        BLO     %BT11                   ; yes, try the next
        ; This is the best match so far
        STMIA   sp, {r5,r6}             ; Remember page number & details ptr
        MOV     lr, r9                  ; Remember base address
        B       %BT11
12
        Pull    "r5,r6"
        CMP     r6, #-1                 ; did we find anything?
        BEQ     %FT40
        LDMDB   r6,{r9,r10}
        ADD     r12, r9, #1
        Push    "r12"                   ; Remember that we've processed up to here

        ; Now process this entry
        MOV     r10, r10, LSR #12
        ADD     r10, r9, r10, LSL #12   ; Add amount of unused space between current and start of block.
        SUB     r10, r10, r2            ; size = size + (physaddr - current)
20
        SUBS    r4, r4, #4              ; Reduce shift.
        MOVCS   r3, r3, LSR #4          ; If more space in current word then shift it.
        STRCC   r3, [r1], #4            ; Otherwise, store current word
        MOVCC   r3, #0                  ;   and start a new one.
        MOVCC   r4, #28

        CMP     r2, r9                  ; If not reached start of block then page is not present.
        ORRCC   r3, r3, #(NotPresent :OR: NotAvailable) :SHL: 28
        BCC     %FT30
        LDR     lr, [r7, r5, LSL #3]    ; Page is there so get PPL and determine if it's available or not.
        TST     lr, #PageFlags_Unavailable
        ORREQ   r3, r3, #DRAM_Pattern :SHL: 28
        ORRNE   r3, r3, #(DRAM_Pattern :OR: NotAvailable) :SHL: 28
        ADD     r5, r5, #1              ; Increment page count.
30
        ADD     r2, r2, #&1000          ; Increase current address.
        SUBS    r10, r10, #&1000        ; Decrease size of block.
        BGT     %BT20                   ; Stop if no more block left.

        B       %BT10

40
        TEQ     r3, #0                          ; If not stored last word then
        MOVNE   r3, r3, LSR r4                  ;   put bits in correct position
        ADDNE   r2, r2, r4, LSL #BitShift       ;   adjust current address
        RSBNE   r4, r4, #32                     ;   rest of word is not present
        LDRNE   lr, =NotPresent :OR: NotAvailable
        ORRNE   r3, r3, lr, LSL r4
        STRNE   r3, [r1], #4                    ;   and store word.

        ; End of last block of DRAM to r11 is not present.
        MOV     r6, r0
        ADD     lr, r11, #1
        RSBS    r2, r2, lr
        MOVNE   r0, r1
        LDRNE   r1, =NotPresent :OR: NotAvailable
        MOVNE   r2, r2, LSR #ByteShift
        BLNE    memset

        ; If softloaded (ie ROM image is wholely within DRAM area returned
        ; by HAL_PhysInfo), mark that as unavailable DRAM.
        LDR     r0, =ZeroPage
        LDR     r0, [r0, #ROMPhysAddr]
        LDR     r1, [sp, #4]
        CMP     r0, r6
        ADDHS   lr, r0, #OSROM_ImageSize*1024
        SUBHS   lr, lr, #1
        CMPHS   r11, lr
        ADDHS   r0, r1, r0, LSR #ByteShift
        LDRHS   r1, =DRAM_Pattern :OR: NotAvailable
        MOVHS   r2, #(OSROM_ImageSize*1024) :SHR: ByteShift
        BLHS    memset

        EXIT


fill_words
        STR     r3, [r1], #4
        SUBS    r2, r2, #1
        BNE     fill_words
        MOV     pc, lr


;----------------------------------------------------------------------------------------
; MemoryAmounts
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     8 (reason code)
;                       8-11    1=return amount of DRAM (excludes any soft ROM)
;                               2=return amount of VRAM
;                               3=return amount of ROM
;                               4=return amount of I/O space
;                               5=return amount of soft ROM (ROM loaded into hidden DRAM)
;                       12-31   reserved (set to 0)
;
;       Out:    r1 = number of pages of the specified type of memory
;               r2 = page size (in bytes)
;
;       Return the amount of the specified type of memory.
;
MemoryAmounts   ROUT
        Entry   "r3"

 [ HAL
        BICS    lr, r0, #&FF            ; Get type of memory required (leave bits 12-31, non-zero => error).
        CMP     lr, #6:SHL:8
        ADDCC   pc, pc, lr, LSR #8-2
        NOP
        B       %FT99                   ; Don't understand 0 (so the spec says).
        B       %FT10                   ; DRAM
        B       %FT20                   ; VRAM
        B       %FT30                   ; ROM
        B       %FT40                   ; I/O
        B       %FT50                   ; Soft ROM

10
        LDR     r1, =ZeroPage
        LDR     r3, [r1, #VideoSizeFlags]
        TST     r3, #OSAddRAM_IsVRAM
        MOVNE   r3, r3, LSR #12         ; Extract size from flags when genuine VRAM
        MOVNE   r3, r3, LSL #12
        MOVEQ   r3, #0
        LDR     r1, [r1, #RAMLIMIT]
        SUB     r1, r1, r3              ; DRAM = RAMLIMIT - VRAMSize
        B       %FT97
20
        LDR     r1, =ZeroPage
        LDR     r1, [r1, #VideoSizeFlags]
        TST     r1, #OSAddRAM_IsVRAM
        MOVNE   r1, r1, LSR #12
        MOVNE   r1, r1, LSL #12         ; VRAM = VRAMSize
        MOVEQ   r1, #0
        B       %FT97
30
        Push    "r0, sb, ip"
        AddressHAL
        MOV     r0, #PhysInfo_HardROM
        SUB     sp, sp, #8
        MOV     r2, sp
        CallHAL HAL_PhysInfo
        LDMIA   sp!, {r0-r1}
        SUB     r1, r1, r0              ; ROM = ROMPhysTop - ROMPhysBot
        Pull    "r0, sb, ip"
        B       %FT97
40
        LDR     r1, =ZeroPage
        LDR     r1, [r1, #IOAllocLimit]
        LDR     r3, =IO
        SUB     r1, r3, r1              ; IO = IO ceiling - IO floor
        B       %FT97
50
        Push    "r0"
        MOV     r0, #8
        SWI     XOS_ReadSysInfo         ; Are we softloaded?
        Pull    "r0"
        AND     r1, r1, r2
        ANDS    r1, r1, #1:SHL:4        ; Test OS-runs-from-RAM flag
        MOVNE   r1, #OSROM_ImageSize*1024
        B       %FT97
97
        MOV     r1, r1, LSR #12         ; Return as number of pages.
        MOV     r2, #4*1024             ; Return page size.
        EXIT
99
        ADRL    r0, ErrorBlock_BadParameters
      [ International
        BL      TranslateError
      |
        SETV
      ]
        EXIT
 |
        BICS    lr, r0, #&FF            ; Get type of memory required (leave bits 12-31, non-zero => error).
        BEQ     %FT30                   ; Don't understand 0 (so the spec says).

        TEQ     lr, #5:SHL:8            ; Check for soft ROM
        BNE     %FT10
        LDR     r1, =L1PT
        MOV     r2, #ROM
        ADD     r1, r1, r2, LSR #(20-2)   ; L1PT address for ROM
        LDR     r2, [r1]
        MOV     r2, r2, LSR #12
        TEQ     r2, #PhysROM :SHR: 12     ; see if we have hard or soft ROM
        MOVEQ   r1, #0                    ; no soft ROM
        MOVNE   r1, #OSROM_ImageSize*1024 ; this much soft ROM
        B       %FT20

10
        TEQ     lr, #4:SHL:8            ; Check for IO space.
        LDREQ   r1, =136*1024*1024      ; Just return 136M (includes VIDC and EASI space).
        BEQ     %FT20

        TEQ     lr, #3:SHL:8            ; Check for ROM.
        LDREQ   r1, =OSROM_ImageSize*1024
        BEQ     %FT20

        TEQ     lr, #2:SHL:8            ; Check for VRAM.
        MOVEQ   r1, #0                  ; Return amount of VRAM.
        LDREQ   r1, [r1, #VRAMSize]
        BEQ     %FT20

        TEQ     lr, #1:SHL:8            ; Check for DRAM.
        BNE     %FT30
        MOV     r1, #0
        LDR     lr, [r1, #RAMLIMIT]
        LDR     r1, [r1, #VRAMSize]
        SUB     r1, lr, r1              ; Return amount of RAM - amount of VRAM.

20
        MOV     r1, r1, LSR #12         ; Return as number of pages.
        MOV     r2, #4*1024             ; Return page size.
        EXIT

30
        ADRL    r0, ErrorBlock_BadParameters
        SETV
        EXIT
 ]


;----------------------------------------------------------------------------------------
; MemoryIOSpace
;
;       In:     r0 = 9 (reason code with flag bits 8-31 clear)
;               r1 = controller ID
;                       bit     meaning
;                       0-7     controller sequence number
;                       8-31    controller type:
;                               0 = EASI card access speed control
;                               1 = EASI space(s)
;                               2 = VIDC1
;                               3 = VIDC20
;                               4 = S space (IOMD,podules,NICs,blah blah)
;                               5 = Extension ROM(s)
;                               6 = Tube ULA
;                               7-31 = Reserved (for us)
;                               32 = Primary ROM
;                               33 = IOMD
;                               34 = FDC37C665/SMC37C665/82C710/SuperIO/whatever
;                               35+ = Reserved (for ROL)
;
;       Out:    r1 = controller base address or 0 if not present
;
;       Return the location of the specified controller.
;

 [ HAL
MemoryIOSpace   ROUT
        Entry   "r0,r2,r3,sb,ip"
        AddressHAL
        CallHAL HAL_ControllerAddress
        CMP     r0, #-1
        MOVNE   r1, r0
        ADREQL  r0, ErrorBlock_BadParameters
        STREQ   r0, [sp, #0]
        SETV    EQ
        EXIT
 |
MemoryIOSpace   ROUT
        Entry   "r2"

        AND     r2, r1, #&FF            ; Get sequence number.
        MOV     r1, r1, LSR #8          ; Get controller type.
        CMP     r1, #6                  ; Make sure it's in range.
        ADRCSL  r0, ErrorBlock_BadParameters
        SETV    CS
        EXIT    CS

        ADR     lr, controller_types
        LDR     r1, [lr, r1, LSL #2]    ; Get base address or offset to table.
        TEQ     r1, #0                  ; If not present or
        CMPNE   r1, #1024               ;    not offset to table then
        EXIT    GE                      ;    return.

        ADD     lr, lr, r1              ; Point to table indexed by sequence number.
        LDR     r1, [lr], #4            ; Get sequence number limit.
        CMP     r2, r1                  ; Make sure it's in range.
        BCS     %FT20
        LDR     r1, [lr, r2, LSL #2]    ; Get base address.
        EXIT

controller_types
        DCD     IOMD_Base + IOMD_ECTCR  ; Expansion card timing control.
        DCD     easi_space_table - controller_types
        DCD     0
        DCD     VIDC
        DCD     IOMD_Base
        DCD     0

easi_space_table
        DCD     8                       ; Maximum of 8 expansion cards.
        DCD     PhysSpace + IOMD_EASI_Base0
        DCD     PhysSpace + IOMD_EASI_Base1
        DCD     PhysSpace + IOMD_EASI_Base2
        DCD     PhysSpace + IOMD_EASI_Base3
        DCD     PhysSpace + IOMD_EASI_Base4
        DCD     PhysSpace + IOMD_EASI_Base5
        DCD     PhysSpace + IOMD_EASI_Base6
        DCD     PhysSpace + IOMD_EASI_Base7
 ] ; HAL

;----------------------------------------------------------------------------------------
; MemoryFreePoolLock
;
;
;       In:     r0 bits 0..7  = 10 (reason code 10)
;               r0 bit  8     = 1 if call is by Wimp (reserved for Acorn use), 0 otherwise
;               r0 bits 9..31 reserved (must be 0)
;               r1 = 0  to resume any background actions that may manipulate FreePool mapping
;                       (currently, this is just VRAMRescue)
;                  = 1  to suspend any background actions that may manipulate FreePool mapping
;                  all other values reserved (undefined behaviour)
;
;       Out:    r1 = previous state (1=suspended, 0=not suspended)
;
; A suspend call from the Wimp will be taken to mean that the whole FreePool is locked, and
; memory cannot be moved (supports Wimp_ClaimFreeMemory)
;
; VRAM rescue doesn't happen in this Kernel, but we still support the call for the Wimp.
;
MemoryFreePoolLock ROUT
        Push    "r2,r3,lr"
        LDR     r3,=ZeroPage
        LDRB    lr,[r3,#VRAMRescue_control]
        TEQ     r1,#0
        BNE     %FT10
;flush TLB(s) for resume - needed for Wimp resume, precaution for other - no cache flush
;since Free Pool is uncached
 [ HAL
        Push    "r0"
        ARMop   TLB_InvalidateAll,,,r3
        Pull    "r0"
 |
        ARM_flush_TLB r2
 ]
10
        TST     r0,#&100                    ;is it Wimp?
        MOVEQ   r2,#VRRc_suspend
        MOVNE   r2,#VRRc_wimp_lock
        TEQ     r1,#0
        AND     r1,lr,r2                    ;previous state
        BICEQ   lr,lr,r2
        ORRNE   lr,lr,r2
        STRB    lr,[r3,#VRAMRescue_control] ;set/clear relevant bit
        TEQ     r1,#0
        MOVNE   r1,#1
        Pull    "r2,r3,pc"

;----------------------------------------------------------------------------------------
;PCImapping - reserved for Acorn use (PCI manager)
;
; See code on Ursula branch


;----------------------------------------------------------------------------------------
;RecommendPage
;
;       In:     r0 bits 0..7  = 12 (reason code 12)
;               r0 bit 8 = 1 if region must be DMAable
;               r0 bits 9..31 = 0 (reserved flags)
;               r1 = size of physically contiguous RAM region required (bytes)
;               r2 = log2 of required alignment of base of region (eg. 12 = 4k, 20 = 1M)
;
;       Out:    r3 = page number of first page of recommended region that could be
;                    grown as specific pages by dynamic area handler (only guaranteed
;                    if grow is next page claiming operation)
;        - or error if not possible (eg too big, pages unavailable)
;
RecommendPage ROUT
        Push    "r0-r2,r4-r11,lr"
        CMP     r2,#30
        BHI     RP_failed         ;refuse to look for alignments above 1G
        ANDS    r11,r0,#1:SHL:8   ;convert flag into something usable in the loop
        MOVNE   r11,#OSAddRAM_NoDMA
;
        ADD     r1,r1,#&1000
        SUB     r1,r1,#1
        MOV     r1,r1,LSR #12
        MOVS    r1,r1,LSL #12     ;size rounded up to whole no. of pages
;
        CMP     r2,#12
        MOVLO   r2,#12            ;log2 alignment must be at least 12 (4k pages)
        MOV     r0,#1
        MOV     r4,r0,LSL r2      ;required alignment-1
;
        LDR     r0,=ZeroPage+PhysRamTable
        MOV     r3,#0            ;page number, starts at 0
        LDR     r5,=ZeroPage+CamEntriesPointer
        LDR     r5,[r5]
        ADD     r5,r5,#4         ; [r5,<page no.>,LSL #3] addresses flags word in CAM
        LDMIA   r0!,{r7,r8}      ;address,size of video chunk (skip this one)
;
RP_nextchunk
        ADD     r3,r3,r8,LSR #12 ;page no. of first page of next chunk
        LDMIA   r0!,{r7,r8}      ;address,size of next physical chunk
        CMP     r8,#0
        BEQ     RP_failed
        TST     r8,r11           ;ignore non-DMA regions if bit 8 of R0 was set
        BNE     RP_nextchunk
;
        MOV     r8,r8,LSR #12
        ADD     r6,r7,r4
        MOV     r8,r8,LSL #12
        SUB     r6,r6,#1         ;round up
        MOV     r6,r6,LSR r2
        MOV     r6,r6,LSL r2
        SUB     r6,r6,r7         ;adjustment to first address of acceptable alignment
        CMP     r6,r8
        BHS     RP_nextchunk     ;negligible chunk
        ADD     r7,r3,r6,LSR #12 ;first page number of acceptable alignment
        SUB     r9,r8,r6         ;remaining size of chunk
;
;find first available page
RP_nextpage
        CMP     r9,r1
        BLO     RP_nextchunk
        LDR     r6,[r5,r7,LSL #3]  ;page flags from CAM
        ;must not be marked Unavailable or Required
        TST     r6,#PageFlags_Unavailable :OR: PageFlags_Required
        BEQ     RP_checkotherpages
RP_nextpagecontinue
        CMP     r9,r4
        BLS     RP_nextchunk
        ADD     r7,r7,r4,LSR #12   ;next page of suitable alignment
        SUB     r9,r9,r4
        B       RP_nextpage
;
RP_checkotherpages
        ADD     r10,r7,r1,LSR #12
        SUB     r10,r10,#1         ;last page required
RP_checkotherpagesloop
        LDR     r6,[r5,r10,LSL #3] ;page flags from CAM
        TST     r6,#PageFlags_Unavailable :OR: PageFlags_Required
        BNE     RP_nextpagecontinue
        SUB     r10,r10,#1
        CMP     r10,r7
        BHI     RP_checkotherpagesloop
;
;success!
;
        MOV     r3,r7
        Pull    "r0-r2,r4-r11,pc"

RP_failed
        MOV     r3,#0
        ADR     r0,RP_error
        SETV
        STR     r0,[sp]
        Pull    "r0-r2,r4-r11,pc"

RP_error
        DCD     0
        DCB     "No chunk available (OS_Memory 12)",0
        ALIGN

;----------------------------------------------------------------------------------------
;MapIOpermanent - map IO space (if not already mapped) and return logical address
;
;       In:     r0 bits 0..7  = 13 (reason code 13)
;               r0 bit  8     = 1 to map bufferable space (0 is normal, non-bufferable)
;               r0 bit  9     = 1 to map cacheable space (0 is normal, non-cacheable)
;               r0 bits 10..12 = 0 (reserved for cache policy)
;               r0 bits 13..15 = 0 (reserved flags)
;               r0 bit  16    = 1 to doubly map
;               r0 bit  17    = 1 if access privileges specified
;               r0 bits 18..23 = 0 (reserved flags)
;               r0 bits 24..27 = access privileges (if bit 17 set)
;               r0 bits 28..31 = 0 (reserved flags)
;               r1 = physical address of base of IO space required
;               r2 = size of IO space required (bytes)
;
;       Out:    r3 = logical address of base of IO space
;        - or error if not possible (no room)
;
MapIOpermanent ROUT
        Push    "r0-r2,r12,lr"
        MOV     lr, r0
        TST     lr, #1:SHL:8        ;test bufferable bit
        MOVNE   r0, #L1_B
        MOVEQ   r0, #0
        TST     lr, #1:SHL:9        ;test cacheable bit
        ORRNE   r0, r0, #L1_C
        TST     lr, #1:SHL:16
        ORRNE   r0, r0, #MapInFlag_DoublyMapped
        TST     lr, #1:SHL:17
        ANDNE   lr, lr, #2_1111:SHL:24
        ADRNEL  r12, PPLTransL1
        LDRNE   r12, [r12, lr, LSR #22]
        ORRNE   r0, r0, #MapInFlag_APSpecified
        ORRNE   r0, r0, r12
        BL      RISCOS_MapInIO
        MOV     r3, r0
        CMP     r3, #0              ;MOV,CMP rather than MOVS to be sure to clear V
        Pull    "r0-r2,r12,pc",NE
        ADR     r0, MIp_error
        SETV
        STR     r0, [sp]
        Pull    "r0-r2,r12,pc"

MIp_error
        DCD     0
        DCB     "No room for IO space (OS_Memory 13)",0
        ALIGN

;----------------------------------------------------------------------------------------
;AccessPhysAddr - claim temporary access to given physical address (in fact,
;                 controls access to the 1Mb aligned space containing the address)
;                 The access remains until the next AccessPhysAddr or until a
;                 ReleasePhysAddr (although interrupts or subroutines may temporarily
;                 make their own claims, but restore on Release before returning)
;
;       In:     r0 bits 0..7  = 14 (reason code 14)
;               r0 bit  8     = 1 to map bufferable space, 0 for unbufferable
;               r0 bits 9..31 = 0 (reserved flags)
;               r1 = physical address
;
;       Out:    r2 = logical address corresponding to phys address r1
;               r3 = old state (for ReleasePhysAddr)
;
; Use of multiple accesses: it is fine to make several Access calls, and
; clean up with a single Release at the end. In this case, it is the old state
; (r3) of the *first* Access call that should be passed to Release in order to
; restore the state before any of your accesses. (The r3 values of the other
; access calls can be ignored.)
;
AccessPhysAddr ROUT
        Push    "r0,r1,r12,lr"
        TST     r0, #&100           ;test bufferable bit
        MOVNE   r0, #L1_B
        MOVEQ   r0, #0
        SUB     sp, sp, #4          ; word for old state
        MOV     r2, sp              ; pointer to word
        BL      RISCOS_AccessPhysicalAddress
        MOV     r2, r0
        Pull    r3                  ; old state
        Pull    "r0,r1,r12,pc"

;----------------------------------------------------------------------------------------
;ReleasePhysAddr - release temporary access that was claimed by AccessPhysAddr
;
;       In:     r0 bits 0..7  = 15 (reason code 15)
;               r0 bits 8..31 = 0 (reserved flags)
;               r1 = old state to restore
;
ReleasePhysAddr
        Push    "r0-r3,r12,lr"
        MOV     r0, r1
        BL      RISCOS_ReleasePhysicalAddress
        Pull    "r0-r3,r12,pc"

;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
;                       0-7     16 (reason code)
;                       8-15    1=cursor/system/sound
;                               2=IRQ stack
;                               3=SVC stack
;                               4=ABT stack
;                               5=UND stack
;                               6=Soft CAM
;                               7=Level 1 page tables
;                               8=Level 2 page tables
;                               9=HAL workspace
;                               10=Kernel buffers
;                               11=HAL uncacheable workspace
;                       16-31   reserved (set to 0)
;
;       Out:    r1 = base of area
;               r2 = address space allocated for area (whole number of pages)
;               r3 = actual memory used by area (whole number of pages)
;               all values 0 if not present, or incorporated into another area
;
;       Return size of various low-level memory regions
MemoryAreaInfo ROUT
        Entry   "r0"
        MOV     r1, #0
        MOV     r2, #0
        MOV     r3, #0
        MOV     lr, r0, LSR #8
        AND     lr, lr, #&FF
        CMP     lr, #(MAI_TableEnd - MAI_TableStart)/4
        ADDLO   pc, pc, lr, LSL #2
        B       %FT70
MAI_TableStart
        B       %FT70
        B       MAI_CursSysSound
        B       MAI_IRQStk
        B       MAI_SVCStk
        B       MAI_ABTStk
        B       MAI_UNDStk
        B       MAI_SoftCAM
        B       MAI_L1PT
        B       MAI_L2PT
        B       MAI_HALWs
        B       MAI_Kbuffs
        B       MAI_HALWsNCNB
MAI_TableEnd

70
        ADRL    r0, ErrorBlock_BadParameters
        SETV
        STR     r0, [sp, #Proc_RegOffset+0]
        EXIT

MAI_CursSysSound
        LDR     r1, =CursorChunkAddress
        MOV     r2, #32*1024
        MOV     r3, r2
        EXIT

MAI_IRQStk
 [ IRQSTK < CursorChunkAddress :LOR: IRQSTK > CursorChunkAddress+32*1024
        LDR     r1, =IRQStackAddress
        MOV     r2, #IRQSTK-IRQStackAddress
        MOV     r3, r2
 ]
        EXIT

MAI_SVCStk
        LDR     r1, =SVCStackAddress
        MOV     r2, #SVCSTK-SVCStackAddress
        MOV     r3, r2
        EXIT

MAI_ABTStk
 [ No26bitCode
        LDR     r1, =ABTStackAddress
        MOV     r2, #ABTSTK-ABTStackAddress
        MOV     r3, r2
 ]
        EXIT

MAI_UNDStk
        LDR     r1, =UNDSTK :AND: &FFF00000
        LDR     r2, =UNDSTK :AND: &000FFFFF
        MOV     r3, r2
        EXIT

MAI_SoftCAM
        LDR     r0, =ZeroPage
        LDR     r1, [r0, #CamEntriesPointer]
        LDR     r2, =CAMspace
        LDR     r3, [r0, #SoftCamMapSize]
        EXIT

MAI_L1PT
        LDR     r1, =L1PT
        MOV     r2, #16*1024
        MOV     r3, r2
        EXIT

MAI_L2PT
        LDR     r0, =ZeroPage
        LDR     r1, =L2PT
        MOV     r2, #4*1024*1024
        LDR     r3, [r0, #L2PTUsed]
        EXIT

MAI_HALWs
 [ HAL
        LDR     r0, =ZeroPage
        LDR     r1, =HALWorkspace
        MOV     r2, #HALWorkspaceSize
        LDR     r3, [r0, #HAL_WsSize]
 ]
        EXIT

MAI_HALWsNCNB
 [ HAL
        LDR     r0, =ZeroPage
        LDR     r1, =HALWorkspaceNCNB
        MOV     r2, #32*1024
        LDR     r3, [r0, #HAL_Descriptor]
        LDR     r3, [r3, #HALDesc_Flags]
        ANDS    r3, r3, #HALFlag_NCNBWorkspace
        MOVNE   r3, r2
 ]
        EXIT

MAI_Kbuffs
        LDR     r1, =KbuffsBaseAddress
        MOV     r2, #KbuffsMaxSize
        LDR     r3, =(KbuffsSize + &FFF) :AND: :NOT: &FFF
        EXIT

        END