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

; MEMC interface file - MEMC1 version

; Created by TMD 10-Aug-90

VInit   * &03600000
VStart  * &03620000
VEnd    * &03640000
CInit   * &03660000
; SStart  * &03680000
; SEnd    * &036A0000
; SPtr    * &036C0000

; *****************************************************************************
;
;       SetDAG - Program DMA address generator R1 with physical address R0
;
; in:   r0 = physical address
;       r1 = index of DMA address generator to program, as defined in vdudecl
;
; out:  All registers preserved, operation ignored if illegal
;

SetDAG  ENTRY   "r0"
        CMP     r1, #MEMCDAG_MaxReason
        EXIT    HI
        ADR     r14, DAGAddressTable
        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC1
        MOV     r0, r0, LSR #4                  ; bottom 4 bits irrelevant
        CMP     r0, #(1 :SHL: 15)               ; ensure in range
        ORRCC   r14, r14, r0, LSL #2
        STRCC   r14, [r14]                      ; any old data will do
        EXIT

        GBLA    DAGIndex
DAGIndex SETA   0

        MACRO
        DAGTab  $reason, $address
        ASSERT  ($reason)=DAGIndex
        &       $address
DAGIndex SETA   DAGIndex + 1
        MEND

DAGAddressTable
        DAGTab  MEMCDAG_VInit, VInit
        DAGTab  MEMCDAG_VStart, VStart
        DAGTab  MEMCDAG_VEnd, VEnd
        DAGTab  MEMCDAG_CInit, CInit

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; CAM manipulation utility routines

BangCamUpdate ROUT

; R2 = CAM entry no
; R3 = logaddr
; R9 = current MEMC value
; R11 = PPL
; set and update tables

        MOV     R4, #0
        LDR     R4, [R4, #CamEntriesPointer]
        ORR     r0, r3, r11, LSL #28  ; top nibble is PPL
        STR     r0, [R4, R2, LSL #2]

BangCam

; r0 corrupted
; r1 corrupted
; R2 = CAM entry no
; R3 = logaddr
; r4 corrupted
; r5 spare!
; r6 corrupted
; r7, r8 spare
; R9 = current MEMC value
; r10 spare
; R11 = PPL
; r12 spare

        AND     R4, R9, #&C           ; pagesize
        ADR     R0, PageMangleTable
        LDR     R0, [R0, R4]          ; load data table pointer
        MOV     R4, #0
01      LDR     R1, [R0], #4
        CMP     R1, #-1
        BEQ     %FT02
        AND     R6, R2, R1
        LDR     R1, [R0], #4
        CMP     R1, #0
        RSBMI   R1, R1, #0
        ORRPL   R4, R4, R6, LSL R1
        ORRMI   R4, R4, R6, LSR R1
        B       %BT01

02      LDR     R1, [R0], #4
        CMP     R1, #-1
        BEQ     %FT03
        AND     R6, R3, R1
        LDR     R1, [R0], #4
        CMP     R1, #0
        RSBMI   R1, R1, #0
        ORRPL   R4, R4, R6, LSL R1
        ORRMI   R4, R4, R6, LSR R1
        B       %BT02

03      ORR     R4, R4, #CAM
        ORR     R4, R4, R11, LSL #8     ; stuff in PPL
        STR     R4, [R4]                ; and write it
        MOV     PC, LR

; Data to drive CAM setting

PageMangleTable
        &       PageMangle4K
        &       PageMangle8K
        &       PageMangle16K
        &       PageMangle32K

; For each page size, pairs of masks and shift factors to put the bits in the
; right place. Two sets: operations on Physical Page Number, operations on
; Logical Page Number.

; Shifts are Shift Left values (<<). Each section terminated by -1

PageMangle4K
; PPN:
        &       2_011111111
        &       0                       ; bits in right place
        &       -1
; LPN:
        &       2_1100000000000:SHL:12
        &       (11-12)-12              ; LPN[12:11] -> A[11:10]
        &       2_0011111111111:SHL:12
        &       (22-10)-12              ; LPN[10:0 ] -> A[22:12]
        &      -1

PageMangle8K
; PPN:
        &       2_010000000
        &       7-7                     ; PPN[7]   -> A[7]
        &       2_001000000
        &       0-6                     ; PPN[6]   -> A[0]
        &       2_000111111
        &       6-5                     ; PPN[5:0] -> A[6:1]
        &       -1
; LPN:
        &       2_110000000000:SHL:13
        &       (11-11)-13              ; LPN[11:10] -> A[11:10]
        &       2_001111111111:SHL:13
        &       (22-9)-13               ; LPN[9:0]   -> A[22:13]
        &       -1

PageMangle16K
; PPN:
        &       2_010000000
        &       7-7                     ; PPN[7]   -> A[7]
        &       2_001100000
        &       1-6                     ; PPN[6:5] -> A[1:0]
        &       2_000011111
        &       6-4                     ; PPN[4:0] -> A[6:2]
        &       -1
; LPN:
        &       2_11000000000:SHL:14
        &       (11-10)-14              ; LPN[10:9] -> A[11:10]
        &       2_00111111111:SHL:14
        &       (22-8)-14               ; LPN[8:0]  -> A[22:14]
        &       -1

PageMangle32K
; PPN:
        &       2_100000000
        &       12-8                    ; PPN[8] -> A[12]
        &       2_010000000
        &       7-7                     ; PPN[7] -> A[7]
        &       2_001000000
        &       1-6                     ; PPN[6] -> A[1]
        &       2_000100000
        &       2-5                     ; PPN[5] -> A[2]
        &       2_000010000
        &       0-4                     ; PPN[4] -> A[0]
        &       2_000001111
        &       6-3                     ; PPN[3:0] -> A[6:3]
        &       -1
; LPN:
        &       2_1100000000:SHL:15
        &       (11-9)-15               ; LPN[9:8] -> A[11:10]
        &       2_0011111111:SHL:15
        &       (22-7)-15               ; LPN[7:0] -> A[22:15]
        &       -1

PageSizes
        &       4*1024                  ; 0 is 4K
        &       8*1024                  ; 4 is 8K
        &       16*1024                 ; 8 is 16
        &       32*1024                 ; C is 32

PageShifts
        =       12, 13, 0, 14           ; 1 2 3 4
        =       0,  0,  0, 15           ; 5 6 7 8

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_UpdateMEMC: Read/write MEMC1 control register

SSETMEMC ROUT

        AND     r10, r0, r1
        MOV     r12, #0
        TEQP    pc, #SVC_mode+I_bit+F_bit
        LDR     r0, [r12, #MEMC_CR_SoftCopy] ; return old value
        BIC     r11, r0, r1
        ORR     r11, r11, R10
        BIC     r11, r11, #&FF000000
        BIC     r11, r11, #&00F00000
        ORR     r11, r11, #MEMCADR
        STR     r11, [r12, #MEMC_CR_SoftCopy]
        STR     r11, [r11]
        TEQP    pc, #SVC_mode+I_bit
        ExitSWIHandler

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;       ClearPhysRAM - Routine to clear "all" memory
;
; While this routine is running, keyboard IRQs may happen. For this reason
; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM
; 0..31 where the IRQ workspace is.
;
; r7 contains memory speed and must be preserved
; r8 contains page size and must be preserved
; r9 contains MEMC control register and must be preserved
;

ClearPhysRAM ROUT
        MOV     r0, #0
        MOV     r1, #0
        MOV     r2, #0
        MOV     r3, #0
        MOV     r4, #0
        MOV     r5, #0
        MOV     r6, #0
        MOV     r11, #0
        MOV     r12, #PhysRam
        CMP     r13, #512*1024
        ADDEQ   r10, r12, #(512-64)*1024 ; get address that's logram 0
        ADDNE   r10, r12, #512*1024
        ADD     r13, r13, #PhysRam      ; end of memory
        ADD     r12, r12, #4*8          ; skip minimal startup workspace
10      CMP     r12, R10
        ADDEQ   r12, r12, #4*8          ; skip physram that's logram 0
        STMNEIA r12!, {r0-r6, r11}
        CMP     r12, r13
        BNE     %BT10
        SUB     r13, r13, #PhysRam

        LDR     r0, =OsbyteVars + :INDEX: LastBREAK
        MOV     r1, #&80
        STRB    r1, [r0]                ; flag the fact that RAM cleared
        MOV     pc, lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;       InitMEMC - Initialise memory controller
;

InitMEMC ROUT
        LDR     R0, ResetMemC_Value
        STR     R0, [R0]     ; set ROM access times, refresh on flyback, no DMA
        MOV     pc, lr

; -> MemSize

; (non-destructive) algorithm to determine MEMC RAM configuration
;
; Dave Flynn and Alasdair Thomas
; 17-March-87
;
; Spooling checkered by NRaine and SSwales !
; 8MByte check bodged in by APT
;
; NOTE: Routines MemSize and TimeCPU are called by the power-on test software,
; so their specifications MUST not change.
;
; Set MEMC for 32-k page then analyse signature of possible
; external RAM configurations...
; The configurations are:
;
; Ram Size    Page Size    Configuration    (Phys RAM) Signature
;--------------------------------------------------------------------
;  16MByte      32k        4*32*1Mx1         A13,A20,A21,A22,A23,A23.5 distinct
;  16MByte      32k        16*8*256kx4       A13,A20,A21,A22,A23,A23.5 distinct
;
;  12MByte      32k        3*32*1Mx1         A13,A20,A21,A22,A23 OK, A23.5 fail
;  12MByte      32k        12*8*256kx4       A13,A20,A21,A22,A23 OK, A23.5 fail
;
;   8MByte      32k        2*32*1Mx1         A13,A20,A21,A22 distinct, A23 fail
;   8MByte      32k         8*8*256kx4       A13,A20,A21,A22 distinct, A23 fail
;
;   4Mbyte      32k          32*1Mx1         A13,A21,A20 distinct, A22,A23 fail
;   4Mbyte      32k         4*8*256kx4       A13,A21,A20 distinct, A22,A23 fail
;
;   2Mbyte      32k    expandable 2*8*256kx4 A13,A20 distinct, A21 fails
;   2Mbyte ???  16k      fixed 2*8*256kx4    A13,A21 distinct, A20 fails
;
;   1Mbyte       8k          32*256kx1       A13,A20 fail, A19,A18,A12 distinct
;   1Mbyte       8k           8*256kx1       A13,A20 fail, A19,A18,A12 distinct
;   1Mbyte       8k          4*8*64kx4       A13,A20 fail, A19,A18,A12 distinct
;
; 512Kbyte       8k    expandable 2*8*64kx4  A13,A20,A19 fail, A12,A18 distinct
; 512Kbyte       4k      fixed 2*8*64kx4     A13,A20,A12 fail, A19,A18 distinct
;
; 256Kbyte       4K           8*64kx4        A13,A20,A12,A18 fail, A21,A19 ok
; 256Kbyte       4K          32*64kx1        A13,A20,A12,A18 fail, A21,A19 ok
;

Z_Flag     * &40000000

; MemSize routine... enter with 32K pagesize set
; R0 returns page size
; R1 returns memory size
; R2 returns value set in MEMC
; uses R3-R7

MemSize ROUT
        MOV     r7, lr
        MOV     r0, #PhysRam
        ADD     r1, r0, #A13
        BL      DistinctAddresses
        BNE     %10
        ADD     r1, r0, #A21
        BL      DistinctAddresses
        MOVNE   r0, #Page32K
        MOVNE   r1, #2048*1024
        BNE     MemSizeDone

        MOV     r0, #PhysRam
        ADD     r1, r0, #4*1024*1024
        BL      DistinctAddresses
        MOVNE   r0, #Page32K
        MOVNE   r1, #4*1024*1024
        BNE     MemSizeDone

        MOV     r0, #PhysRam
        ADD     r1, r0, #8*1024*1024
        BL      DistinctAddresses
        MOVNE   r0, #Page32K
        MOVNE   r1, #8*1024*1024
        BNE     MemSizeDone

        MOV     r0, #PhysRam
        ADD     r1, r0, #12*1024*1024
        BL      DistinctAddresses
        MOV     r0, #Page32K
        MOVNE   r1, #12*1024*1024
        MOVEQ   r1, #16*1024*1024
        B       MemSizeDone

10      ADD     r1, r0, #A20
        BL      DistinctAddresses
        BNE     %20
        MOV     r0, #Page16K
        MOV     r1, #2048*1024
        B       MemSizeDone

20      ADD     r1, r0, #A19
        BL      DistinctAddresses
        BEQ     %30
        MOV     r0, #Page8K
        MOV     r1, #512*1024
        B       MemSizeDone

30      ADD     r1, r0, #A18
        BL      DistinctAddresses
        BEQ     %40
        MOV     r0, #Page4K
        MOV     r1, #256*1024
        B       MemSizeDone

40      ADD     r1, r0, #A12
        BL      DistinctAddresses
        BEQ     %50
        MOV     r0, #Page4K
        MOV     r1, #512*1024
        B       MemSizeDone

50      MOV     r0, #Page8K
        MOV     r1, #1024*1024

MemSizeDone
        LDR     r2, ResetMemC_Value
        BIC     r2, r2, #&C
        ORR     r2, r2, r0
        STR     r2, [r2]                        ; set MEMC to right state
        ADR     r3, PageSizes
        LDR     r0, [r3, r0]                    ; r0 = convert from page size indicator into actual page size (in bytes)
        MOV     pc, r7


; DistinctAddresses routine...
; r0,r1 are the addresses to check
; uses r2-5
; writes interleaved patterns (to prevent dynamic storage...)
; checks writing every bit low and high...
; return Z-flag set if distinct

DistinctAddresses ROUT
        LDR     r2, [r0] ; preserve
        LDR     r3, [r1]
        LDR     r4, Pattern
        STR     r4, [r0] ; mark first
        MOV     r5, r4, ROR #16
        STR     r5, [r1] ; mark second
        LDR     r5, [r0]
        CMP     r5, r4 ; check first
        BNE     %10    ; exit with Z clear
        LDR     r5, [r1] ; check second
        CMP     r5, r4, ROR #16 ; clear Z if not same
        BNE     %10
; now check inverse bit writes
        STR     r4, [r1] ; mark second
        MOV     r5, r4, ROR #16
        STR     r5, [r0] ; mark first
        LDR     r5, [r1]
        CMP     r5, r4 ; check second
        BNE     %10   ; exit with Z clear
        LDR     r5, [r0] ; check first
        CMP     r5, r4, ROR #16 ; clear Z if not same
10      STR     r3, [r1] ; restore
        STR     r2, [r0]
        ORREQ   lr, lr, #Z_Flag
        BICNE   lr, lr, #Z_Flag
        MOVS    pc, lr

Pattern
        &       &AAFF5500 ; shiftable bit check pattern

; init state with masked out page size

ResetMemC_Value
        & &E010C :OR: MEMCADR       ; slugged ROMs + flyback refresh only + 32K page

; Constants
;
A21 * 1:SHL:21
A20 * 1:SHL:20
A19 * 1:SHL:19
A18 * 1:SHL:18
A13 * 1:SHL:13
A12 * 1:SHL:12

Page32K * &C ; in MEMC control reg patterns...
Page16K * &8
Page8K  * &4
Page4K  * &0


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r0-r6 trashable
;       r9 = Current MEMC CR

; Out   r9 MEMC value with slowest ROM speed, correct pagesize
;       r7 processor speed in kHz, tbs -> MEMC1a

ncpuloops * 1024 ; don't go longer than 4ms without refresh !
nmulloops * 128

TimeCPU ROUT

        BIC     r9, r9, #3 :SHL: 8
        STR     r9, [r9]                ; turn off refresh for a bit

; Time CPU/Memory speed

        LDR     r1, =&7FFE              ; 32K @ 2MHz = ~16ms limit
        MOV     r3, #IOC

        MOV     r0, r1, LSR #8
        STRB    r1, [r3, #Timer1LL]
        STRB    r0, [r3, #Timer1LH]
        LDR     r0, =ncpuloops
        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
        B       %FT10                   ; Looks superfluous, but is required
                                        ; to get ncpuloops pipeline breaks

10      SUBS    r0, r0, #1              ; 1S
        BNE     %BT10                   ; 1N + 2S

        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
        LDRB    r2, [r3, #Timer1CL]
        LDRB    r0, [r3, #Timer1CH]
        ADD     r2, r2, r0, LSL #8      ; count after looping is ...

        SUB     r2, r1, r2              ; decrements !
        MOV     r2, r2, LSR #1          ; IOC clock decrements at 2MHz

; Time CPU/MEMC Multiply time

        MOV     r4, #-1                 ; Gives worst case MUL

        MOV     r0, r1, LSR #8
        STRB    r1, [r3, #Timer1LL]
        STRB    r0, [r3, #Timer1LH]
        LDR     r0, =nmulloops
        STRB    r0, [r3, #Timer1GO]     ; start the timer NOW
        B       %FT20                   ; Looks superfluous, but is required
                                        ; to get nmulloops pipeline breaks

20      MUL     r5, r4, r4              ; 1S + 16I
        MUL     r5, r4, r4              ; 1S + 16I
        SUBS    r0, r0, #1              ; 1S
        BNE     %BT20                   ; 1N + 2S

        STRB    r0, [r3, #Timer1LR]     ; latch count NOW
        LDRB    r4, [r3, #Timer1CL]
        LDRB    r0, [r3, #Timer1CH]
        ADD     r4, r4, r0, LSL #8      ; count after looping is ...

        SUB     r4, r1, r4              ; decrements !
        MOV     r4, r4, LSR #1          ; IOC clock decrements at 2MHz

        ORR     r9, r9, #1 :SHL: 8      ; set refresh on flyback
        STR     r9, [r9]                ; restore MEMC state a.s.a.p.

; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle

        LDR     r0, =4*(8*500/1000)*ncpuloops*1000
        DivRem  r7, r0, r2, r1          ; r2 preserved
        MOV     r0, #&80                ; At 8 MHz and below, run fast ROMs
        LDR     r1, =8050               ; Over 8 MHz, need medium ROMs
        CMP     r7, r1
        MOVHI   r0, #&40
        LDR     r1, =13000              ; Over 13 MHz, need slowest ROMs
        CMP     r7, r1
        MOVHI   r0, #&00
        ORR     r9, r9, r0
        STR     r9, [r9]                ; Set ROM speed appropriately

 ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------

        MOV     r4, r4, LSL #10         ; *1024 to get resolution on divide
        DivRem  r0, r4, r2, r1
        LDR     r1, =1100               ; Cutoff point; MEMC1 longer than this
        CMP     r0, r1
        ORRLO   r7, r7, #1 :SHL: 16     ; Note MEMC1a prescence

        MOV     pc, lr

; Typical figures give (in ROM at 8MHz):

; MEMC1  2048 CPU, 2432 MEMC -> MUL ratio 1216
; MEMC1a 2048       864                    432

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        END