MEMC2 24.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
; 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