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

; MEMC interface file - MEMC2 version

; Created by TMD 10-Aug-90

PhysRAML2PT * &02000000 + (512+64)*1024

; Synonyms

VInit                   *       MEMC2Address + MEMC2_VINITe
VStart                  *       MEMC2Address + MEMC2_VSTRTe
VEnd                    *       MEMC2Address + MEMC2_VENDe
CInit                   *       MEMC2Address + MEMC2_CINIT

; *****************************************************************************
;
;       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,r1"
        CMP     r1, #MEMCDAG_MaxReason
        EXIT    HI
        ADR     r14, DAGAddressTable
        LDR     r14, [r14, r1, LSL #2]          ; load base address in MEMC2
        MOV     r1, r0, LSR #16                 ; r1 is top 16 bits
        EOR     r0, r0, r1, LSL #16             ; and r0 is bottom 16 bits
        BIC     r0, r0, #&0F                    ; bits 0..3 must be clear
        BIC     r1, r1, #&F000                  ; and bits 28..31 must be clear
        STMIA   r14, {r0, r1}                   ; atomic update (we believe)
        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 - Update CAM entry and soft copy
;
; This part of the routine has to do more work on MEMC2
;
; First look in the CamEntries table to find the logical address L this physical page is
; currently allocated to. Then check in the Level 2 page tables to see if page L is currently
; at page R2. If it is, then map page L to be inaccessible, otherwise leave page L alone.
; Then map logical page R3 to physical page R2.
;
; in:   r2 = physical page number
;       r3 = logical address
;       r9 = current MEMC1 control register (irrelevant on MEMC2)
;       r11 = PPL
;
; out:  r0, r1, r4, r6 corrupted
;       r2, r3, r5, r7-r12 preserved
;
; NB Use of stack is allowed in this routine

BangCamUpdate ROUT
        MOV     r1, #0
        LDR     r1, [r1, #CamEntriesPointer]
        LDR     r0, [r1, r2, LSL #2]            ; r0 = current logaddress + PPL for phys page r2
        ORR     r4, r3, r11, LSL #28            ; new entry for CamEntries
        STR     r4, [r1, r2, LSL #2]            ; update

        BIC     r0, r0, #&F0000000              ; just get logical address
        LDR     r1, =PhysRAML2PT                ; point to page tables
        LDR     r4, [r1, r0, LSR #11]           ; get physical page + PPL for this logical page
        TEQ     r2, r4, LSR #3                  ; see if still there
        BNE     %FT10                           ; if not there, then just put in new page

        Push    "r3, r14"
        MOV     r3, r0                          ; map out old page at this logical address
        MOV     r0, #0                          ; physical page 0 but PPL(MEMC2)=0 ie no access, not even for me!
        BL      BangL2PT                        ; map page out
        Pull    "r3, r14"
10

;       and drop thru to ...

; **************************************************************************************
;
;       BangCam - Update CAM entry, but not soft copy
;
; This routine maps a physical page to a given logical address
; For MEMC2, I assume that the physical page was previously not mapped
; anywhere else - on MEMC1 it would automatically unmap any logical
; address that the physical page was previously at, but on MEMC2 it won't
;
; in:   r2 = physical page number
;       r3 = logical address
;       r9 = current MEMC1 control register (irrelevant on MEMC2)
;       r11 = PPL
;
; out:  r0, r1, r4, r6 corrupted
;       r2, r3, r5, r7-r12 preserved
;
; NB Can't use stack - there might not be one!

BangCam
        ADR     r0, PPLTrans            ; translate MEMC1 PPL to MEMC2 PPL
        LDRB    r0, [r0, r11]
        ORR     r0, r0, r2, LSL #3      ; value to store in level 2 page table
                                        ; is PPL :OR: (phys page number << 3)

        LDR     r1, =PhysRAML2PT        ; point to level 2 page tables

BangL2PT                                ; internal entry point used only by BangCamUpdate
        BICS    r4, r3, #(3 :SHL: 11)   ; ensure going to be on word boundary (EQ => logical page zero)
        STR     r0, [r1, r4, LSR #11]   ; update level 2 page table

        MOV     r6, #MEMC2Address
        STREQ   r0, [r6, #MEMC2_SuperPageZero] ; if logical page 0 then update special entry

        MOV     r0, #0                  ; now flush the TLB
        STR     r0, [r6, #MEMC2_Flush]

        MOV     pc, lr

PPLTrans
        =       6                       ; R any W any
        =       3                       ; R any W sup
        =       2                       ; R sup W sup
        =       2                       ; R sup W sup

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]

; We now have to mimic the relevant bits of the MEMC1 control register
;
; bits 0,1 => unused
; bits 2,3 => page size, irrelevant since always 8K
; bits 4,5 => low ROM access time, irrelevant since this has to be fixed
; bits 6,7 => hi  ROM access time, -----------------""------------------
; bits 8,9 => DRAM refresh control, irrelevant (refresh must always be on)
; bit 10   => Video/cursor DMA enable, corresponds to bit venbe of VATT
;              (and possibly bit venbo of IATT for interlaced displays)
;              Unfortunately VATT (and IATT) is a write-only register. Later on
;              we might have a soft copy of these, but for now just write whole
;              register.
; bit 11   => Sound DMA enable, ignore for now
; bit 12   => OS mode, ignore

; Program all of VATT
;
; vdis = 0 (don't disable DMA after one buffer)
; venbe = (bit 10 of r11)
; vrnw = 1 (read from RAM, not write)
; vmske = 0 (no interrupts at end of buffer)

        MOV     r12, # (0 * VATT_vdis) + (0 * VATT_venbe) + (1 * VATT_vrnw) + (0 * VATT_vmske)
        TST     r11, # (1 :SHL: 10)
        ORRNE   r12, r12, # VATT_venbe
        MOV     r10, #MEMC2Address
        STR     r12, [r10, #MEMC2_VATT]

        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.
;
; On MEMC2 it also has to avoid the pages where the level 2 page tables are
;
; 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     r11, #0
        MOV     r4, #PhysRam
        CMP     r13, #512*1024
        ADDEQ   r10, r4, #(512-64)*1024 ; get address that's logram 0
        ADDNE   r10, r4, #512*1024
        ADD     r13, r13, #PhysRam      ; end of memory
        ADD     r12, r4, #PhysRAML2PT-PhysRam
        ADD     r4, r4, #4*8            ; skip minimal startup workspace
10
        CMP     r4, r10
        ADDEQ   r4, r4, #4*8            ; skip physram that's logram 0
        CMP     r4, r12
        ADDEQ   r4, r4, #32*1024        ; skip 32K of L2PT
        STMNEIA r4!, {r0-r3}
        CMP     r4, 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
        MOV     r0, # MEMC2Address
        MOV     r1, # Control_romfast
        STR     r1, [r0, #MEMC2_Control]        ; make ROMS go fast

        ADR     r1, ClockTimingTable            ; initialise CLK block
        LDMIA   r1, {r2-r5}
        ADD     r1, r0, #MEMC2_clkj0
        STMIA   r1, {r2-r5}

        ADR     r1, DRAMTimingTable             ; initialise DRAM block
        LDMIA   r1, {r2-r8}
        ADD     r1, r0, #MEMC2_rwt0
        STMIA   r1, {r2-r8}

        LDR     r1, rtype_value
        STR     r1, [r0, #MEMC2_rtype]

        LDR     r1, refr_value
        STR     r1, [r0, #MEMC2_refr]

        MOV     r1, # (PhysRAML2PT-PhysRam)/(8*1024)            ; level 1 entry (paged) for 0..16M
        MOV     r2, # (PhysRAML2PT+8*1024-PhysRam)/(8*1024)     ; level 1 entry (paged) for 16M..32M
        MOV     r3, # Prot_UrwSRW+(L1D_RAM :SHL: 3)+(L1D_RAM :SHL: 5)+(L1D_RAM :SHL: 7)+(L1D_RAM :SHL: 9)+(0 :SHL: 11)
                                                                ; level 1 entry (direct) for 32M..48M (all RAM, base 0)
        LDR     r4, = Prot_URwSRW+(L1D_IO :SHL: 3)+(L1D_PROG :SHL: 5)+(L1D_ROM :SHL: 7)+(L1D_ROM :SHL: 9)+(0 :SHL: 11)
                                                                ; level 1 entry (direct) for 48M..64M (IO,PROG,ROM,ROM)
        ADD     r5, r0, #MEMC2_Level1+L1_Paged+L1_Sec0
        ADD     r6, r5, #4*2*16                 ; cycle thru all 4 bus masters and (USR,SPV)
        ADD     r7, r5, #(L1_Direct+L1_Sec2)-(L1_Paged+L1_Sec0)
10
        STMIA   r5, {r1,r2}                     ; set up the paged sections 0,1
        STMIA   r7, {r3,r4}                     ; set up the direct sections 2,3
        ADD     r5, r5, #16
        ADD     r7, r7, #16
        TEQ     r5, r6                          ; have we got to the end ?
        BNE     %BT10

; Now turn on the translation, but don't set up the level 2 page tables until later,
; when we know the DRAM multiplex option

        LDR     r1, = Control_ton + Control_l1on + Control_romfast
        STR     r1, [r0, #MEMC2_Control]

; now set up VINC

        MOV     r1, #&10                ; low bits
        MOV     r2, #&00                ; high bits
        ADD     r3, r0, #MEMC2_VINC
        STMIA   r3, {r1,r2}

        MOV     pc, lr

ClockTimingTable
J0      &       &F200   ; 3   / 2
J1      &       &F200   ; 3   / 2
RSPEED  &       &1C00   ; 3   / 3
ISPEED  &       &B800   ; 2.5 / 2.5

DRAMTimingTable
RWT0    &       &2A320
RRD0    &       &2AC80
RSQ0    &       &0B200
B0Dummy &       0
RWT1    &       &2A320
RRD1    &       &2AC80
RSQ1    &       &0B200

rtype_value     &       &3333   ; Bank Type [x,bank,s1,s0] * 4 ; set largest memory type by default

refr_value      &       &1086   ; Enable refresh, refresh length = 10 hclk ticks, refresh period = 12us

; -> 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
 [ {TRUE}                               ; now work on different configurations, but only bank zero
        MOV     r7, lr

; first find out appropriate rtype value
; initial routine has set DRAM type to 3

        MOV     r0, #PhysRam
        ADD     r1, r0, #A9             ; if A9 ghosts
        BL      DistinctAddresses
        MOVNE   r0, #2_00               ; then type 00
        BNE     %FT10

        ADD     r1, r0, #A11            ; else if A11 ghosts
        BL      DistinctAddresses
        MOVNE   r0, #2_01               ; then type 01
        BNE     %FT10

        ADD     r1, r0, #A12            ; else if A12 ghosts
        BL      DistinctAddresses
        MOVNE   r0, #2_01               ; then type 01
        MOVEQ   r0, #2_11               ; else type 11
10
        LDR     r1, rtype_value
        BIC     r1, r1, #2_11
        ORR     r1, r1, r0
        MOV     r0, #MEMC2Address
        STR     r1, [r0, #MEMC2_rtype]

; having set up the DRAM multiplexing correctly, we can now zap the L2PT
; to no access for any page

        LDR     r1, =PhysRAML2PT
        ADD     r2, r1, #2*8*1024               ; two L2PT tables at the moment
        MOV     r3, #0                          ; page 0, no access
        MOV     r4, #0
        MOV     r5, #0
        MOV     r6, #0
15
        STMIA   r1!,{r3-r6}
        TEQ     r1, r2
        BNE     %BT15

        STR     r3, [r0, #MEMC2_SuperPageZero]  ; don't forget super page zero

; now find out the memory size

        MOV     r0, #PhysRam
        MOV     r6, #256*1024
20
        ADD     r1, r0, r6
        BL      DistinctAddresses       ; try next address line
        BNE     %FT30                   ; if ghosts or not there then finish
        MOV     r6, r6, LSL #1
        CMP     r6, #16*1024*1024       ; give up if we've got 16MBytes or more
        BCC     %BT20
30
        MOV     r1, r6
        LDR     r2, ResetMemC_Value
        BIC     r2, r2, #&C
        ORR     r2, r2, #Page8K
        MOV     r0, #8*1024             ; fixed 8K page size
        MOV     pc, r7
 |
        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
        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
;
A0      *       1 :SHL: 00
A1      *       1 :SHL: 01
A2      *       1 :SHL: 02
A3      *       1 :SHL: 03
A4      *       1 :SHL: 04
A5      *       1 :SHL: 05
A6      *       1 :SHL: 06
A7      *       1 :SHL: 07
A8      *       1 :SHL: 08
A9      *       1 :SHL: 09
A10     *       1 :SHL: 10
A11     *       1 :SHL: 11
A12     *       1 :SHL: 12
A13     *       1 :SHL: 13
A14     *       1 :SHL: 14
A15     *       1 :SHL: 15
A16     *       1 :SHL: 16
A17     *       1 :SHL: 17
A18     *       1 :SHL: 18
A19     *       1 :SHL: 19
A20     *       1 :SHL: 20
A21     *       1 :SHL: 21
A22     *       1 :SHL: 22
A23     *       1 :SHL: 23
A24     *       1 :SHL: 24
A25     *       1 :SHL: 25
A26     *       1 :SHL: 26
A27     *       1 :SHL: 27
A28     *       1 :SHL: 28
A29     *       1 :SHL: 29
A30     *       1 :SHL: 30
A31     *       1 :SHL: 31

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, bit 16 => can do STM to I/O (ie MEMC1a, MEMC2), bit 17 => MEMC2

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

TimeCPU ROUT
 [ {TRUE}
;       fudge it for now
        LDR     r7, =6000 + (3 :SHL: 16) ; pretend 6MHz system, and MEMC2
        MOV     pc, lr
 |
        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