;*************************************************************************
;** mmu.library                                                         **
;**                                                                     **
;** a system library for arbitration and control of the MC68K MMUs      **
;**                                                                     **
;** © 1998 THOR-Software, Thomas Richter                                **
;** No commercial use, reassembly, modification without prior, written  **
;** permission of the authors.                                          **
;** Including this library in any commercial software REQUIRES a        **
;** written permission and the payment of a small fee.                  **
;**                                                                     **
;** This is an internal header file, do not depend on anything here.    **
;** Use the official include files.                                     **
;** Distributed only for the mmu.library development group for private  **
;** use.                                                                **
;**                                                                     **
;**---------------------------------------------------------------------**
;** Block: MMU Table Parser for 68030 and 68851                         **
;*************************************************************************

;FOLD Includes
        include inc:macros.asm
        include mu_lib.i
        include mu_context.i
        include mu_alerts.i
;ENDFOLD
;FOLD External references
        xref DefineMapping
        xref MUAlert
;ENDFOLD

        section main_code,code

        machine mc68020

;FOLD Parse030
;*************************************************
;** Parse030                                    **
;** The parser for the 030 MMU tables           **
;** *a0=the Context to fill out                 **
;** *a1=the pointer to MMUConfig                **
;**                                             **
;** Returns:                                    **
;** d0 =0 if out of memory.                     **
;** d0 =1 if O.K., including if no useful       **
;**                table is available           **
;*************************************************
        xdef Parse030
Parse030:
        saveregs d2-d7/a2-a5

        move.l a0,a2                            ;keep context

        btst #7,mcf_translationctrl(a1)         ;MMU enabled?
        beq .okexit                             ;if not, ignore that anyways

        moveq #3,d2                             ;descriptor type is long
        lea mcf_userrootpointer(a1),a3          ;this is the root level
        bfextu mcf_userrootpointer(a1){30:2},d1 ;get the descriptor type
        btst #1,d1                              ;must be short or long. Everything else is not useful
        beq .okexit

        btst #0,mcf_translationctrl(a1)         ;function code lookup enabled ? (Huh?)
        beq.s .nofclup
.mualert:
        pea AN_IllegalMMU
        bsr MUAlert
        moveq #0,d0
        bra .exit
.nofclup:
                                                ;*a3 is now the root level
                                                ;d2 the descriptor type of this level
        btst #1,3(a3)                           ;must be a table descriptor, everything else is ignored
        beq .okexit

        swap d2
        sub.l a4,a4                             ;address to make a table search for
        move.w #$7fff,d2                        ;Limit=No limit
        do
         move.l a4,d3                           ;keep the address
         moveq #-1,d4                           ;current level in the tree: read the initial shift the first time
         move.l d2,d5                           ;Descriptor type
         swap d4
         moveq #1,d6                            ;address range described by this level
         clr.w d4
         moveq #0,d7                            ;accumulated flags
         swap d4
         move.l a3,a5
         do
          moveq #0,d1
          cmp.w #4,d4                           ;already at page level ?
          bge.s .indirect                       ;if so, we're examining an indirect descriptor. Read directly.

          move.b mulib_LevelABits(a6,d4.w),d0
          beq.s .noshift
          lsl.l d0,d3                           ;ignore these bits, shift levels from last level off the word
          ror.l d0,d6                           ;calculate address range
.noshift:

          addq.w #1,d4                          ;next level of the tree
          move.b mulib_LevelABits(a6,d4.w),d0   ;read the size of the next level
          bne.s .isthere
          move.w #4,d4                          ;empty, so use the page level and abort
          move.b mulib_PageBits(a6),d0
.isthere:
          bfextu d3{0:d0},d1                    ;get the index from the address
.indirect:
          swap d4
          move.w d4,d0                          ;page index to d0
          move.w d1,d4                          ;save new page index: keep for next iteration
          swap d4

          bclr #15,d5                           ;upper or lower bound ?
          bne.s .lower
                                                ;it's an upper bound
          cmp.w d5,d0
          bhi.s .invalidpage
          bra.s .nicepage
.lower:
          cmp.w d5,d0
          blo.s .invalidpage
.nicepage:

          btst #16,d5                           ;is this a short or a long table ?
          beq.s .short

          lea (a5,d0.w*8),a5                    ;it's a long table
                                                ;make a limit check
          move.w (a5),d5                        ;get limit
.isvalid:
          btst #0,2(a5)                         ;supervisor only?
          beq.s .checkprotection
          bset #mapp_supervisoronly,d7          ;this MUST be zero at the root level
          bra.s .checkprotection
.short:
          lea (a5,d0.w*4),a5
          move.w #$7fff,d5                      ;no limit
.checkprotection:
          btst #2,3(a5)                         ;write protected ?
          beq.s .nowriteprot
          bset #mapp_writeprotected,d7          ;accumulate write protection
                                                ;this MUST be zero at the root level
.nowriteprot:
          bfextu (a5){30:2},d0                  ;get descriptor type for this level of the tree
          tst.b d0                              ;which type
          beq.s .invalidpage                    ;this descriptor type is invalid
          cmp.b #1,d0                           ;page descriptor = early termination ?
          beq.s .termination

          btst #16,d5                           ;is this a long or a short table ?
          beq.s .shorttable
          move.l 4(a5),d1                       ;here long
          bra.s .gotnext
.shorttable:
          move.l (a5),d1
.gotnext:
          swap d5
          and.l mulib_LevelAMask(a6,d4.w*4),d1  ;mask out bits( #4 is indirect mask)
          move.b d0,d5                          ;keep type
          move.l d1,a5                          ;next level tree pointer
          swap d5
         loop

.invalidpage:
         move.l a4,a0
         lea (a4,d6.l),a1                       ;upper and lower address to exclude
         move.l #(1<<mapp_invalid),d0
         moveq #0,d1
         bsr _SetDescriptor
         beq .exit                              ;exit on error
         bra .endloop

.termination:
         btst #6,3(a5)                          ;cache inhibit?
         beq.s .keepcache
         bset #mapp_cacheinhibit,d7
         bra.s .setnow
.keepcache:
         btst #mapp_writeprotected,d7           ;WP and copyback is SILLY
         bne.s .setnow
         bset #mapp_copyback,d7                 ;the '030 doesn't know this, but this avoids rebuilding the complete default mapping
.setnow:

         btst #16,d5                            ;short or long?
         beq .shortpage
         move.l 4(a5),d1                        ;get page address
         cmp.w #4,d4                            ;are we at page level ?
         bge.s .sub                             ;yes, so ignore limit and continue as usual
         move.l d6,d1                           ;alignment to d1
         move.b mulib_LevelABits(a6,d4.w),d0
         ror.l d0,d1                            ;d1 is the table size at the next lower level

         moveq #0,d5
         addq.w #1,d4                           ;next level of the tree
         move.b mulib_LevelABits(a6,d4.w),d0    ;read the size of the next level
         bne.s .lowerlevel
         move.b mulib_PageBits(a6),d0           ;if at the bottom of the tree, use the page size
.lowerlevel:
         bset d0,d5                             ;number of pages in here
         subq.w #1,d5                           ;first page is lower

         move.w (a5),d0                         ;get limit
         bclr #15,d0                            ;is this a lower limit ?
         bne.s .lowerlimit
                                                ;it's an upper limit
         cmp.w d5,d0
         bhi.s .gotlimit
         move.w d0,d5                           ;reduce upper limit
         bra.s .gotlimit
.lowerlimit:
         cmp.w d5,d0
         bhi .mualert                           ;this limit seems to be nonsense
         swap d5
         move.w d0,d5
         swap d5
.gotlimit:                                      ;d5.hi is now the lower limit, d5.lo the lower limit
         move.l d1,d4                           ;d4 is the page size one level below
         move.l a4,a0
         addq.w #1,d5
         lea (a4,d6.l),a1
         move.l #(1<<mapp_invalid),d0           ;mark this as invalid
         bsr _SetDescriptor
         beq.s .exit
         move.w d5,d0                           ;get upper
         move.l 4(a5),d1                        ;get page address
         mulu.w d4,d0                           ;limit
         swap d5
         lea (a4,d0.l),a1                       ;to a0
         mulu.w d4,d5                           ;set lower limit
         move.l d7,d0                           ;accumulated flags
         and.l mulib_PageMask(a6),d1            ;mask out status bits
         lea (a4,d5.l),a0                       ;lower to a0
         sub.l a4,d1                            ;get difference
         beq.s .noindirectlong
         bset #mapp_indirect,d0                 ;will be relocated elsewhere
.noindirectlong:
         bsr _SetDescriptor
         beq.s .exit
         bra.s .endloop
.shortpage:
         move.l (a5),d1
.sub:
         move.l a4,a0
         lea (a4,d6.l),a1
         and.l mulib_PageMask(a6),d1            ;mask out status bits
         move.l d7,d0                           ;accumulated flags
         sub.l a0,d1                            ;get difference
         beq.s .noindirect
         bset #mapp_indirect,d0                 ;will be relocated elsewhere
.noindirect:
         bsr _SetDescriptor
         beq.s .exit
.endloop:
         add.l d6,a4                            ;next address
         cmp.l mulib_UpperEnd(a6),a4
        while ne
.okexit:
        moveq #1,d0
.exit:
        loadregs
        rts

_SetDescriptor:
        reserve map_len

        subq.l #1,a1
        movem.l d0-d1,map_properties(a7)
        movem.l a0-a1,map_lower(a7)             ;upper, lower
        moveq #-1,d0
        clr.l map_flags(a7)
        move.l d0,map_mask(a7)                  ;mask in everything
        move.l a2,a0
        move.l a7,a1
        bsr DefineMapping
        restore
        rts
;ENDFOLD

