;> MEM4H_SCR
;
; RISC OS 2+ BOOT TEST SOFTWARE.
; MEMORY TEST 4 VERSION H.      BRIAN RICE 12-01-90.
; 04-Apr-90     ArtG    0.1     Added ts_count_cams, improved reporting
; 11-Apr-90     ArtG    0.2     Use RISC OS routine BangCams for
;                               alternate MEMC configurations.
; 17-Apr-90     ArtG    0.3     rationalise page-counting code
;
; This file will be called by MEM6x_SCR for the purposes of assembly.
; This file will perform quick walking bit test on the CAM Entry points.
; The test code for this test was taken from the A680 test code.
;
; The module requires the running of the memory sizing routine used by
; the OS to set up the page size for this module.
;
; This test module was designed to operate on all current and future
; machines. The module is designed to handle up to 512 physical pages
; which is the maximum number of pages in a 16 MByte FOX.
;
; A 16 MB FOX has 4 MEMCs in use, each MEMC is addressed by Bits 7 and
; 12 of the logical to physical address translator. The use of bit 12
; does have a problem in that on machines with 0.5MB of memory this is
; used to define the logical page number. Machine with 1MB or greater bit
; 12 is not used, therefore this test may hit problems on A305's. The
; intention is that A305 owners will upgrade to A310's when upgrading to
; RISC OS 2+.
;
; Because FOX can have up to 4 MEMCs fitted the following addressing is
; used to determine the MEMC accessed, bit 12, bit 7
;                                        0      0 = Master MEMC  = MEMC 0
;                                        0      1 = Slave MEMC 1 = MEMC 1
;                                        1      0 = Slave MEMC 2 = MEMC 2
;                                        1      1 = Slave MEMC 3 = MEMC 3
;
;
; This test will initialise the CAM entries for up to 512 physical pages.
; The physical pages will be mapped to logical page 5. Each page will have
; a copy of test routine vectors and a page marker. The page marker consists
; of the page number and a code to indicate which MEMC was used. The code for
; the MEMC used is as follows :- MEMC 0 0001 1110 = &1E
;                                MEMC 1 0010 1101 = &2D
;                                MEMC 2 0100 1011 = &4B
;                                MEMC 3 1000 0111 = &87
;
; The page marker is arranged as follows &mm5Apppp
;                                          |    |
;                                          |    \-- Page Number &0000 � &01FF.
;                                          \--------MEMC Code as above.
;
; The patterns are chosen so that if two or more MEMCs are accessed
; together and both RAM outputs get enabled onto the data bus simultaneously,
; then there is a reasonable chance that the data returned will show the
; presence of a fault.
;
; When the CAM entries have been initialised the module will then check that
; all the pages are mapped correctly. A simple walking one pattern is used
; to check that the page is not present anywhere else in the memory area.
; This isn't really sufficient, but keeps the test time low.
;
; The tests are performed with the memory protection level set to 0.
;
; This version uses the "my abort" routine in MEM5x_SCR instead of the
; ts_dab_exp0 .. 5 method as taken from the A680 code.
;

ts_rst_msg      =       "RST",0
ts_uni_msg      =       "UDF",0
ts_swi_msg      =       "SWI",0
ts_pab_msg      =       "PAB",0
ts_dab_msg      =       "DAB",0
ts_aex_msg      =       "ADX",0
ts_irq_msg      =       "IRQ",0
ts_fiq_msg      =       "FIQ",0
ts_bxc_msg      =       &85,"@",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
        ALIGN


ts_rst                                          ; Unused exception vectors
        ADR     r4, ts_rst_msg
        B       ts_bad_exception
ts_uni
        ADR     r4, ts_uni_msg
        B       ts_bad_exception
ts_swi
        ADR     r4, ts_swi_msg
        B       ts_bad_exception
ts_pab
        ADR     r4, ts_pab_msg
        B       ts_bad_exception
ts_dab_unexp
        ADR     r4, ts_dab_msg
        B       ts_bad_exception
ts_aex
        ADR     r4, ts_aex_msg
        B       ts_bad_exception
ts_irq
        ADR     r4, ts_irq_msg
        B       ts_bad_exception
ts_fiq
        ADR     r4, ts_fiq_msg
        B       ts_bad_exception


ts_bad_exception
        SUBS    r8, r14, #8                     ; remember aborted instruction
        BL      ts_SendText
        ADR     r4, ts_bxc_msg                  ; display aborted address
        BL      ts_MoreText
        B       Reset


;
ts_rom_base     *       ROM                     ; Base address of the OS ROMS.
ts_phys_mem     *       (32*1024*1024)          ; Physical Memory area.
ts_pagemark     *       &005A0000               ; + phys page number + MEMC code.
ts_pmark_pos    *       32                      ; Position of page mark (avoiding vectors).
ts_cam_base     *       &3800000                ; Base address of the CAM table in MEMC.
ts_vrest        *       &5                      ; Unused page which all pages are mapped to.
ts_MAX_CAMS     *       512                     ; Most CAMs ever expected
ts_memc_codes   =       &1E, &2D, &4B, &87      ; List of the memc_codes to be used.
;
ts_logpages                                 ; List of Logical pages.
                 &       &0001
                 &       &0002
                 &       &0004
                 &       &0008
                 &       &0010
                 &       &0020
                 &       &0040
                 &       &0080
                 &       &0100
                 &       &0200
                 &       &03FF
                 &       &03FE
                 &       &03FD
                 &       &03FB
                 &       &03F7
                 &       &03EF
                 &       &03DF
                 &       &03BF
                 &       &037F
                 &       &02FF
                 &       &01FF
                 &       &0000              ; Terminator for the list.
ts_logpagesend                              ; End of the list.
;
;
; Exception vectors : copied to start of each page to ensure that they will always
; exist on page zero when arbitrary pages are mapped there.
;
ts_vectors
        B       (ts_vectors-ts_start)+ts_rom_base+ts_rst
        B       (ts_vectors-ts_start)+ts_rom_base+ts_uni
        B       (ts_vectors-ts_start)+ts_rom_base+ts_swi
        B       (ts_vectors-ts_start)+ts_rom_base+ts_pab
ts_dab_vector
        B       (ts_vectors-ts_start)+ts_rom_base+ts_dab
        B       (ts_vectors-ts_start)+ts_rom_base+ts_aex
        B       (ts_vectors-ts_start)+ts_rom_base+ts_irq
        B       (ts_vectors-ts_start)+ts_rom_base+ts_fiq


; ***************************************************************************
;
ts_CAM
;
; CAM test (full or partial)
; Start of the CAM test, all physical pages have a copy of the vectors
; so they may be mapped as page 0. Then each page is mapped at a series
; of (walking 1, walking 0) logical pages and tested to be correctly
; mapped. Other pages are set to an unused logical page by set_cam_idle
; to prevent any CAM clashes.
;
; Copy the test vectors and page marker into all the pages.
;
        ROUT                                ; Local Branches.
        MOV     r13, lr                     ; Preserve link register in r13.
        BL      ts_count_CAMs               ; get log2 pagesize
        MOV     r0, #ts_MAX_CAMS            ; r0 = last page to test
        SUB     r0, r0, #1
0       BL      ts_copy_vectors             ; Gosub ts_vectors.
        SUBS    r0, r0, #&01                ; bump down to next page
        BGE     %B0                         ; repeatuntil all pages set.
;
; 'C' pseudocode for the test routine.
;
;       for (i = &1ff; i >= 0; i--)
;               set_cam_idle(i);
;
;       find maximum page number.
;       if (max_page != ts_count_CAMS)
;               report CAM number error
;
;       for (phys = &max_page; phys >= 0; phys--) {
;               for (logp = &logpages[0]; logp < &logpages[sizof(logpages)]; logp++) {
;                       if (*logp == 0) {
;                               set_cam(*logp, phys);
;                               check_mapped(*logp, phys);
;                       } else {
;                               int zphys = (phys + 1) % num_pages;
;                               set_cam(0, zphys);
;                               set_cam(*logp, phys);
;                               check_mapped(*logp, phys);
;                               set_cam_idle(zphys);
;                       }
;               }
;               set_cam_idle(phys);
;       }
;
; Idle the pages.
;
        ROUT                                ; Local Branches.
        MOV     r12, #ts_MAX_CAMS           ; always clear all 512 - just in case 4 MEMCs.
        SUB     r12, r12, #&01              ; Subtract 1 to make max page #.
0       MOV     r1, r12                     ; r1 = page number.
        BL      ts_set_cam_idle
        SUBS    r12, r12, #&01              ; bump to next page downwards
        BGE     %B0                         ; repeatuntil page 0 done
;
; We need to find out what the maximum number of pages is, after running the above routine
; all the pages will have the pagemark programed in to each page. As stated in the intro
; programing the pages from the top down will ensure that, irrespective of the number of
; MEMCs available, that the bottom pages are programed correctly. Therefore if we start
; at the top, read in a page, check it's page number & memc code are correct, if so then
; that is possibly the maximum page number. If not then subtract 1 from the page number and
; try again until a possible good page is found.
;
        ROUT                                ; Local Branches.

        BL      ts_count_CAMs               ; get log2 pagesize to r1
        MOV     r8, #ts_MAX_CAMS            ; r8= max. number of physical pages.
0       SUBS    r8, r8, #&01                ; Subtract 1 to make it r8 - 1 Pages.
        BEQ     ts_bad_CAM_count            ; no pages ? - shouldn't hit this!
;
; Calculate the expected page marker, in r4, for the current page, in r8.
;
        ADR     r4, ts_memc_codes           ; r4 = address of table with the memc codes.
        LDRB    r4, [r4, r8, LSR#7]         ; r4 = Loc pointed to by r4 + (r1 >> 7).
        ORR     r4, r8, r4, LSL #24         ; r4 = page number OR (MEMC code << 24).
        ORR     r4, r4, #ts_pagemark        ; r4 = page id OR magic number
;
; The calculated page marker is now in r4, ref_p_mark.
; Current page in r8 - convert to physical address in r9.
; the pagesize power-of-2 is in r1 (from ts_count_CAMs)
;
        MOV     r9, r8, LSL r1              ; convert PPN to phys offset
        ORR     r9, r9, #ts_phys_mem        ; add offset to start of phys mem
;
; r9 now has the address of the current page - read the page marker for that page.
;
        LDR     r9, [r9, #ts_pmark_pos]     ; r9 = contents of loc pointed to by
                                            ;      r9 + ts_pmark_pos.
;
; Check that read_p_mark is valid.
;
; Either the value read is the expected pagemark, junk (no memory) or an
; aliased pagemark - if it's aliased, then either the memory or the MEMC
; isn't decoded that far.
; Bump down and try a bit lower, until it's OK.
;
        CMP     r4, r9                      ; Is page-mark expected value ?
        BNE     %B0

;
; Found a pagemarker in the proper place. Check that the number of pages that
; appear to be present are the same as the number found by ts_count_CAMs
; (i.e. the memory size / page size).
;
        SUB     r0, r0, #1              ; convert count -> max page number
        CMPS    r0, r8
        BNE     ts_bad_CAM_count
;
; If all is well, we should have the maximum usable page number in r8.
;
; Need to reset page 0 in the CAM entries, currently all pages are mapped to page 5.
; We need to have logical page 0 mapped to physical page 0.
;
        MOV      r0, #&00                   ; r0 = &00, the page to map.
        MOV      r1, #&00                   ; r1 = &00, the page to map to.
        MOV      r2, #&00                   ; r2 = &00, set the protection level.
        BL       ts_set_camp
;
; Check we can still see the data abort vector at physical page zero
; - no good continuing if we can't.
;
        MOV     r0, #ts_phys_mem
        LDR     r0, [r0, #(ts_dab_vector - ts_vectors)]
        LDR     r1, ts_dab_vector
        CMPS    r0, r1
        BNE     ts_bad_dab_vector

;
; Now lets get on with the testing.
;

2       ADRL    r10, ts_logpages            ; logp = &logpages[0]

3       LDR     r0, [r10]                   ; r0 = page to test
        CMP     r0, #&00                    ; last entry ?
        BNE     %F4
        MOV     r1, r8                      ; r1 = r8, page under test
        BL      ts_set_cam                  ; Gosub ts_set_cam.
        LDR     r0, [r10]                   ; r0 current logical test page
        MOV     r1, r8                      ; r1 = current test page
        BL      ts_check_mapped             ; Gosub ts_check_mapped.
        B       %F5

4       ADD     r12, r8, #&01
        BL      ts_count_CAMs               ; get total number of pages
        SUB     r0,r0,#1                    ; make a mask for useable page
        AND     r0,r0,#&7f                  ; numbers - min(128, num_pages)
        AND     r12, r12, r0                ; r12 -> (r12 + 1) masked
        MOV     r0, #&00                    ; to useable page numbers.
        MOV     r1, r12
        BL      ts_set_cam                  ; Setup a page for vectors
        LDR     r0, [r10]                   ; r0 = current logical test page.
        MOV     r1, r8                      ; r1 = current physical test page.
        BL      ts_set_cam                  ; Setup a page to test

        LDR     r0, [r10]                   ; look up logical page again.
        MOV     r1, r8                      ; recall physical page.
        BL      ts_check_mapped             ; check the ts_set_cam worked.
        MOV     r1, r12                     ; unmap the vector page
        BL      ts_set_cam_idle

5       ADD     r10, r10, #&04              ; next entry in test list.
        ADRL    r0, ts_logpagesend          ; r0 = ts_logpagesend.
        CMP     r10, r0                     ; repeat until list of logical
        BLO     %B3                         ; pages all done.

        MOV     r1, r8                      ; unmap the page we just tested
        BL      ts_set_cam_idle

        SUBS    r8, r8, #1                  ; bump phys page counter down.
        ANDS    r8,r8,r8
        BGE     %B2                         ; If r8 >= 0 Then branch back to 2.

        ANDS    r0,r0,#0
        MOV     pc,r13                  ; all done and passed

;
; ****************************************************************************
;
ts_copy_vectors
;
; Copies the vectors to the physical page in r0 (preserved) also copies
; pagemark + phypage.
; Expects r1 (preserved) to hold log2 of pagesize
;
        ROUT                                ; Local Branches.

        ADR     r2, ts_vectors              ; r2 = source address
        LDMIA   r2, {r4-r11}                ; r4 - r11 = loc pointed to by r2, post inc.

        MOV     r3, r0, LSL r1              ; r3 = r0 * 2**r1 .
        ORR     r3, r3, #ts_phys_mem        ; r3 = r3 OR ts_phys_mem.
        STMIA   r3, {r4-r11}                ; loc pointed to by r3, post inc = r4 to r11.
;
; find out which memc is handling the page (r0), then assign the appropiate memc_code.
; Add in the page number and pagemark, then store into the required position in the
; page in question.
;
        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
        LDRB    r2, [r2, r0, LSR#7]         ; r2 = memc code for this phys page.
        ORR     r2, r0, r2, LSL #24         ; OR in phys page number.
        ORR     r2, r2, #ts_pagemark        ; OR in pagemark.
        STR     r2, [r3, #ts_pmark_pos]     ; loc pointed to by r1 + ts_pmark_pos = pagemark.
        MOV     pc, lr                      ; Return to caller.
;
; ****************************************************************************
;
ts_set_cam_idle
;
; This module will program the physical page (r1) to the logical page 5, ts_vrest and
; continue onto the next section ts_set_cam.
;
        ROUT                                ; Local Branches.
        MOV     r0, #ts_vrest               ; r0 = ts_vrest, = unused logical page.
;
; ****************************************************************************
;
ts_set_cam
;
; This module will program the physical page (r1) to the logical page (r0) at
; protection mode 0 and continue onto the next section ts_set_camp.
;
        MOV     r2, #&00                    ; r2 = &00, memory prot level 0.
;
; ****************************************************************************
;
ts_set_camp
;
; This module will map a range the physical pages (r1) to the logical page (r0) and
; set the protection mode (r2). This module will return to the location from where
; either itself or ts_set_cam or ts_set_cam_idle were called from.
;
; Corrupts r0,r1,r2,r3,r4,r6,r9,r11
;
; Calls the RISC OS routine BangCam to do the PPNO, LPNO bit switching.
; First, jumble the registers to suit BangCam ..
;
; r2  = CAM entry (PPNO)
; r3  = logical address
; r9  = current MEMC setting (for pagesize)
; r11 = PPL
;
        MOV     r3,r0           ; logical page number
        MOV     r11,r2          ; protection level
        MOV     r2,r1           ; physical page number
        MOV_fiq r0, r11_fiq     ; MEMC configuration
        MOV     r9, r0          ; keep a copy in r9
        MOV     r1, r9, LSR #2
        AND     r1, r1, #3      ; calculate pagesize shift
        ADD     r1, r1, #12
        MOV     r3, r3, LSL r1  ; convert LPN to logaddr
        B       BangCam         ; return thro' BangCam

;
; ****************************************************************************
;
ts_check_mapped
;
; This routine will check that the CAM has been programed correctly and that the required
; page is responding when asked. A quick test is made to check that other pages are not
; responding as well.
;
; logical page  in r0,
; physical page in r1,
; test that they are the same.
;
; No return value : reports faults directly and returns thro' r13
;
; Uses (corrupts) r0,r1,r2,r3,r4,r5,r6,r7
;
; Find out which memc is handling the page (r1), then assign the appropiate memc_code.
; Add in the page number and pagemark, then compare that pagemark with those found
; in memory at the expected logical and physical addresses.
;
; This code assumes that any system with multiple MEMCs will always have 32K pages.
;
        ROUT                                ; Local Branches.

        MOV     r3, r0                      ; save the current logical pagenumber.
        MOV     r5, lr                      ; Preserve link register in case of Abort.
        ADR     r2, ts_memc_codes           ; r2 = address of table with the memc codes.
        LDRB    r2, [r2, r1, LSR#7]         ; fetch the memc code for this page.
        ORR     r2, r1, r2, LSL #24         ; build the page number into the pagemark
        ORR     r2, r2, #ts_pagemark        ; build in the pagemark magic number
;
; r2 should now have the page_mark for the current page (r1).
; calculate the shift to convert page number to memory offset.
;
        MODE    FIQ_mode
        MOV     r4, r11_fiq, LSR #2             ; pagesize / 4K
        MODE    SVC_mode
        AND     r4, r4, #3
        ADD     r4, r4, #12
;
; if the mapping failed completely, the test might abort
;
        MOV     r6, #&00                    ; r6 = &00, clear expected abort flag.
        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
;
; make the pointers and test the contents
;
        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
        LDR     r0, [r0, #ts_pmark_pos]     ; r0 = contents of loc in r0  + ts_pmark_pos.
        CMP     r6, #94                     ; did that fetch abort ?
        ADREQ   r4, %F14                    ; mapping totally failed
        BEQ     ts_CAM_fail
        MOV     r1, r1, LSL r4              ; r1 = PPN * pagesize.
        ORR     r1, r1, #ts_phys_mem        ; r1 = r1 ORed with ts_phys_mem.
        LDR     r1, [r1, #ts_pmark_pos]     ; r1 = contents of loc in r1  + ts_pmark_pos.
        CMP     r0, r1                      ; Are the read pagemarks equal ??
        ADRNE   r4, %F10
        BNE     ts_CAM_fail                 ; Failed : mapping not equal.
        CMP     r0, r2                      ;
        ADRNE   r4, %F11
        BNE     ts_CAM_fail                 ; Failed : map equal, but corrupt
;
; test that the page doesn't exist anywhere else
;
        MOV     r2, #1
0       EOR     r0, r2, r3                  ; Flip a (walking) bit in the LPN.
        CMP     r0, #ts_vrest               ; Is r0 = ts_vrest ?? Where all the pages are
                                            ; mapped to.
        BEQ     %F1                         ; If r0 = ts_vrest then branch forward to 1.
;
; The following instruction should abort.
;
        MOV     r0, r0, LSL r4              ; r0 = LPN * pagesize.
        MOV     r6, #&00                    ; r6 = &00, clear abort handled flag.
        MOV     r7, #&94                    ; r7 = &94, set abort expected flag.
        LDR     r0, [r0, #ts_pmark_pos]     ; get a possible pagemark from this page.
        CMP     r6, #&94                    ; Did we go thro' the abort handler ?
        BEQ     %F1                         ; If equal then an abort happened, good !
;
; Not aborted - is it page zero, where the vectors live ?
;
        TEQS    r2, r3
        BEQ     %F1                        ; yes - that SHOULDN'T abort
;
; Fault - is the page mapped there the same as our test page ?
;
        CMP     r0, r1
        ADREQ   r4, %F12                   ; Failed : phys page also mapped here
        ADRNE   r4, %F13                   ; Failed : page not unmapped
        EOR     r3, r2, r3                 ; remake the duff LPN for the error display
        B       ts_CAM_fail
                                            ; If equal then no abort happened, not good !!

1       MOV     r2, r2, LSL#1               ; bump to next-bit-set page number
        CMP     r2, #(ts_MAX_CAMS :SHL: 1)  ; Hit number of logical pages ?
        BLT     %B0                         ; If r2 < maximum number then loop again.

        MOV     r7, #0                      ; no longer expecting aborts
        MOV     pc, r5                      ; Return to caller.

;
;       Indicate that CAM mapping test failed (PPN is not at LPN)
;       Display r8, the physical page number and r3, the logical page.
;
;    ***This error exit returns to the CALLER of check_mapped, thro' r13***
;

10
        =       "CAM map",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
11
        =       "CAM pmk",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
12
        =       "CAM als",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
13
        =       "CAM unm",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0
14
        =       "CAM abo",&88,&ff,&ff,&ff,".",&ff,&ff,&ff,&ff,0

        ALIGN


ts_CAM_fail
        MOV     r0, r8, LSL #16         ; physical page #
        LDR     r1, =&ffff
        AND     r1, r1, r3
        ORR     r0, r0, r1              ; add logical page #
        MOV     r8, r0, LSL #4
        MOV     r6, #0                  ; no longer expecting aborts
        ORRS    r0, r0, #1
        MOV     pc, r13

;
; **************************************************************************
;

; Routine to return expected number of physical pages in r0.
; Uses memory size determination from r10_fiq and page mode from r11_fiq.
; Returns pagesize as power-of-two in r1, for pagenumber->address calcs.

ts_count_CAMs

        MODE    FIQ_mode
        MOV     r0,r10_fiq,LSR #12      ; get values determined
        MOV     r1,r11_fiq,LSR #2       ; by MemSize
        MODE    SVC_mode

        AND     r1,r1,#3                ; memory / pagesize
        MOV     r0,r0,LSR r1
        ADD     r1,r1,#12               ; page bit-shift value

        MOV     pc,lr


;
; **************************************************************************
;
        ROUT

;       Indicate that an unexpected number of CAM pages were found.
;
;       Display as "CAM ##    eee.fff"
;
;       where eee is the expected maximum page number (r0), fff is the number
;       of of the highest page actually found (r8).

0
        =       "CAM ##",&89,&ff,&ff,&ff,".",&ff,&ff,&ff,0
        ALIGN

ts_bad_CAM_count
        ADD     r8, r8, r0, LSL #12
        MOV     r8, r8, LSL #8
        ADR     r4, %B0
        ORRS    r0, r0 ,#1
        MOV     pc, r13
;
; **************************************************************************
;

;       Indicate that the DAB vector wasn't visible in physmem

0
        =       "CAM vec",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff,0
        ALIGN

ts_bad_dab_vector
        ADR     r4, %B0
        EOR     r8,r0,r1                ; indicate which bits are lost
        ORRS    r0, r0, #1
        MOV     pc, r13
;
; **************************************************************************

;       Routine to indicate that an unexpected abort was found.

0
        =       "DAB @",&88,&ff,&ff,&ff,&ff,&ff,&ff,&ff,&ff, 0
        ALIGN

ts_unxvect
        ADR     r4, %B0
        SUBS    r8, r14_svc, #8         ; indicate the aborting instruction
        BL      ts_SendText
        ORRS    r0, r0, #1
        MOV     pc, r13



        LTORG

 END