;*************************************************************************
;** 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: Context related operations                                   **
;*************************************************************************

;FOLD Includes
        include inc:macros.asm
        include inc:exec_lib.asm
        include inc:expansion_lib.asm
        include mu_lib.i
        include mu_context.i
;ENDFOLD
;FOLD External References
        xref AllocContextMem
        xref FreeContextMem
;ENDFOLD
;FOLD Defines
MemList =       $142

mh_Attributes   =       $0e
mh_Lower        =       $14
mh_Upper        =       $18

ERTB_MEMLIST    =       $05     ;ConfigReg type is memory
er_Type         =       $10     ;Offset of the type in the configreg
cd_BoardAddr    =       $20
cd_BoardSize    =       $24
;ENDFOLD

        section main_code,code

;FOLD AllocContext
;*************************************************
;** AllocContext                                **
;** allocate a context structure -> d0          **
;** or NULL on error                            **
;*************************************************
        xdef AllocContext
AllocContext:
        saveregs a2-a3/a6
        moveq #ctx_len,d0
        bsr AllocContextMem
        beq.s .exit
        move.l d0,a2
        moveq #map_len,d0
        bsr AllocContextMem
        beq.s .exit                     ;get memory for blank mapping
        move.l d0,a3
        lea ctx_Mapping(a2),a1
        lea ctx_Semaphore(a2),a0
        NewList a1                      ;initialize the Mapping list
        move.l mulib_SysBase(a6),a6
        jsr InitSemaphore(a6)           ;initialize the semaphore

        subq.l #1,map_higher(a3)        ;higher address is 0xfff.fff
        bsetm mapp_blank,map_properties(a3)     ;blank space, access must be emulated
        bsetm ctxf_dirty,ctx_flags(a2)          ;this context lacks a CPU table
        bsetm mapf_dirty,map_flags(a3)          ;CPU Table is yet to be build
        lea ctx_Mapping(a2),a0
        move.l a3,a1
        AddHead

        move.l a2,d0
        bra.s .exit
.free:                                  ;on error
        move.l a2,a1
        bsr FreeContextMem
        moveq #0,d0
.exit:
        loadregs
        rts
;ENDFOLD
;FOLD FreeContext
;*************************************************
;** FreeContext                                 **
;** release the context structure in *a0        **
;*************************************************
        xdef FreeContext
FreeContext:
        saveregs a2
        move.l a0,a2
        do
         lea ctx_Mapping(a2),a0
         RemHead
         tst.l d0
         break.s eq
         move.l d0,a1
         bsr FreeContextMem             ;release the mapping nodes
        loop.s

        move.l a2,a1
        bsr FreeContextMem              ;release the context itself

        loadregs
        rts
;ENDFOLD
;FOLD LockContext
;*************************************************
;** LockContext                                 **
;** Lock the context in *a0                     **
;** does not alter any registers                **
;*************************************************
        xdef LockContext
LockContext:
        saveregs a0/a6
        move.l mulib_SysBase(a6),a6
        lea ctx_Semaphore(a0),a0
        jsr ObtainSemaphore(a6)
        loadregs
        rts
;ENDFOLD
;FOLD UnlockContext
;*************************************************
;** UnlockContext                               **
;** Unlock, i.e. release the semaphore *a0      **
;** does not alter any registers                **
;*************************************************
        xdef UnlockContext
UnlockContext:
        saveregs a0/a6
        move.l mulib_SysBase(a6),a6
        lea ctx_Semaphore(a0),a0
        jsr ReleaseSemaphore(a6)
        loadregs
        rts
;ENDFOLD

;FOLD DefineMapping
;*************************************************
;** DefineMapping                               **
;** add a new mapping for the context *a0       **
;** given in *a1                                **
;** returns FALSE in case of failure            **
;** Does NOT force a rebuild of the MMU tables  **
;** but sets the dirty flags                    **
;** doesn't do any sanity checks                **
;*************************************************
        xdef DefineMapping
DefineMapping:
        saveregs a2-a4/d2-d7

        move.l a0,a2                    ;keep the context
        move.l a1,a3                    ;and the new mapping
        bsr LockContext                 ;need exclusive access

        movem.l map_lower(a3),d2-d3     ;lower bounds
        move.l ctx_Mapping(a2),a4       ;get the first mapping node
        do
         tst.l (a4)                     ;end of the list reached ?
         beq .okdone
         movem.l map_lower(a4),d4-d5    ;get bounds here, too
         move.l d2,d6
         move.l d3,d7
         cmp.l d2,d4
         blo.s .nomax
         move.l d4,d6                   ;get maximum of minima
.nomax:
         cmp.l d3,d5
         bhi.s .nomin
         move.l d5,d7                   ;get minimum of maxima
.nomin:
                                        ;[d6,d7] is now the intersection
         cmp.l d6,d7
         bls .nextchk                   ;intersection emtpy?
                                        ;the intersection is always A PART of the already existing node!
                                        ;check whether we've to modify anything at all!
         movem.l map_Properties(a3),d0/a0       ;properties and delta
         move.l map_Properties(a4),d1
         cmp.l map_Delta(a4),a0
         bne.s .modify                  ;if equal, do not modify this
         eor.l d0,d1
         and.l map_Mask(a3),d0
         beq .next

.modify:
         cmp.l d4,d6                    ;remove a lower part of this block?
         beq.s .noadjustbottom
                                        ;here we've to set aside the lower part of the old block
         moveq #map_len,d0
         bsr AllocContextMem            ;get memory
         beq .fail                      ;on failure: exit
         move.l d0,a1                   ;keep it
         move.l map_Properties(a4),map_Properties(a1)
         bsetm mapf_dirty,map_flags(a4)         ;this is dirty
         move.l d6,d0
         move.l map_UserPtr(a4),map_UserPtr(a1) ;copy properties
         subq.l #1,d0
         move.l map_lower(a4),map_lower(a1)     ;lower is this one
         move.l map_flags(a4),map_flags(a1)     ;copy the flags, too
         move.l d0,map_higher(a1)               ;insert higher
         move.l d6,map_lower(a4)                ;modify lower of this thing
         bsetm ctxf_dirty,ctx_Flags(a2)         ;context is dirty, too
         move.l a4,a0
         InsertBefore                           ;insert that in front of me
         reloop                                 ;rerun with the modified node

.noadjustbottom:                                ;check now the head
         cmp.l d5,d7
         beq.s .noadjusttop

         moveq #map_len,d0
         bsr AllocContextMem                    ;get memory
         beq .fail                              ;on failure: exit
         move.l d0,a1                           ;keep it
         move.l map_Properties(a4),map_Properties(a1)
         bsetm mapf_dirty,map_flags(a4)         ;this is dirty
         move.l d7,d0
         move.l map_UserPtr(a4),map_UserPtr(a1) ;copy properties
         addq.l #1,d0
         move.l map_higher(a4),map_higher(a1)   ;higher is this one
         move.l map_flags(a4),map_flags(a1)     ;copy the flags, too
         move.l d0,map_lower(a1)                ;insert new lower
         move.l d7,map_higher(a4)               ;modify higher of this thing
         bsetm ctxf_dirty,ctx_Flags(a2)         ;context is dirty, too
         move.l a4,a0
         InsertAfter                            ;insert that in behind me
         reloop                                 ;rerun with the modified node

.noadjusttop:                                   ;here: The thing fits perfectly. Insert the modified properties
         move.l map_Properties(a4),d0           ;A: old properties
         move.l map_Properties(a3),d1           ;B: new properties
         move.l map_UserPtr(a3),map_UserPtr(a4)
         eor.l d0,d1                            ;AxB
         bsetm mapf_dirty,map_flags(a4)         ;something was modified
         and.l map_Mask(a3),d1                  ;and M
         eor.l d0,d1                            ;xA
         bsetm ctxf_dirty,ctx_flags(a2)         ;ditto in the context
         move.l d1,map_Properties(a4)           ;=((AxB)^M)xA)=(A^M)v(A^_M)
                                                ;now check if the lower neighbor has the same
                                                ;properties. If so, we can remove it
         move.l 4(a4),a1                        ;get lower
         move.l a1,d0
         tst.l 4(a1)                            ;really available ?
         beq.s .chkupper

         lea map_Properties(a4),a0
         lea map_Properties(a1),a1
         cmp.l (a0)+,(a1)+
         bne.s .chkupper
         cmp.l (a0)+,(a1)+
         bne.s .chkupper
                                                ;here: Identical. Remove lower
         move.l d0,a1                           ;restore lower
         move.l map_flags(a1),d1
         move.l d0,d6
         move.l map_lower(a1),map_lower(a4)     ;carry over the lower boundary
         or.l d1,map_flags(a4)                  ;carry over flags
         Remove                                 ;remove this node
         move.l d6,a1
         bsr FreeContextMem                     ;release it again
.chkupper:
         move.l (a4),a1                         ;get next
         move.l a1,d0
         tst.l (a1)                             ;is there a next
         beq.s .nextchk                         ;no, so we're done

         lea map_Properties(a4),a0
         lea map_Properties(a1),a1
         cmp.l (a0)+,(a1)+
         bne.s .nextchk
         cmp.l (a0)+,(a1)+
         bne.s .nextchk

         move.l d0,a1                           ;restore upper
         move.l map_flags(a1),d1
         move.l d0,d6
         move.l map_higher(a1),map_higher(a4)   ;carry over the lower boundary
         or.l d1,map_flags(a4)                  ;carry over flags
         Remove                                 ;remove this node
         move.l d6,a1
         bsr FreeContextMem                     ;release it again
.nextchk:
         cmp.l d4,d3
         blo.s .okdone
.next:
         move.l (a4),a4                         ;get next mapping segment
        loop

.okdone:
        moveq #1,d0
.fail:
        move.l a2,a0
        bsr UnlockContext
        tst.l d0
        loadregs
        rts
;ENDFOLD
;FOLD BuildDefaultTable
;*************************************************
;** BuildDefaultTable                           **
;**                                             **
;** if no default table is present, build one   **
;** by scanning the ROM,RAM and the extensions  **
;** Context *a0                                 **
;*************************************************
        xdef BuildDefaultTable
BuildDefaultTable:
        saveregs a2/a4-a5
        move.l a0,a2                            ;keep context
        move.l a7,a4                            ;keep stack

        move.l #$f00000,a0
        move.l #$f80000,a1                      ;diagnostic ROM
        move.l #(1<<mapp_rom),d0                ;status
        bsr _AddMap
        beq .fail

        move.l #$01000000,a1
        move.l a1,a0
        sub.l -$14(a0),a0                       ;lower
        move.l #(1<<mapp_rom),d0                ;status
        bsr _AddMap
        beq .fail
                                                ;I don't want to add the 0xf0000000
                                                ;memory area. Undocumented
        move.l a6,a5
        move.l mulib_SysBase(a6),a6
        jsr Forbid(a6)

        moveq #0,d0
        move.l MemList(a6),a0                   ;get list of all memheaders
        do
         tst.l (a0)
         break.s eq
         move.w mh_Attributes(a0),d0            ;get the attributes of this list
         move.l d0,-(a7)                        ;save to stack
         move.l mh_Upper(a0),-(a7)
         move.l mh_Lower(a0),-(a7)              ;save lower
         move.l (a0),a0                         ;get next
        loop.s

        jsr Permit(a6)                          ;done
        move.l a5,a6                            ;restore mubase

        moveq #1,d0
        do
         cmp.l a4,a7
         break.s hs                             ;if all done, return

         move.l #(1<<mapp_copyback),d0          ;enable copyback if FAST
         movem.l (a7)+,a0-a1                    ;get upper, lower
         move.l (a7)+,d1                        ;get attributes
         btst #1,d1                             ;chip mem?
         beq.s .isfast
         move.l #(1<<mapp_cacheinhibit),d0      ;if so, cache inhibited, precise exception model
.isfast:
         bsr _AddMap
        while.s ne
                                                ;here: failure, whatever
        move.l a4,a7                            ;restore stack
        tst.l d0                                ;failure ?
        beq .fail

        sub.l a4,a4                             ;configdev
        do
         move.l mulib_ExpansionBase(a6),a6      ;Expansion list
         move.l a4,a0                           ;start with this configdev
         moveq #-1,d0
         move.l d0,d1                           ;any manufacturer
         jsr FindConfigDev(a6)                  ;find configdevs
         move.l d0,a4                           ;a configdev present
         move.l a5,a6
         tst.l d0
         break.s eq                             ;if not, then exit

         btst #ERTB_MEMLIST,er_Type(a4)         ;is this external memory?
         reloop.s ne                            ;if so, ignore it
         movem.l cd_BoardAddr(a4),a0-a1         ;get address of the board
         adda.l a0,a1                           ;upper size
         move.l #(1<<mapp_cacheinhibit),d0      ;cache inhibited, precise exception model
         bsr _AddMap
         beq .fail
        loop.s

        move.l #$bc0000,a0                      ;actually, $bf0000. Don't be picky
        move.l #$c00000,a1
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap                             ;add the CIAs
        beq .fail

        move.l #$d80000,a0                      ;actually, $dc0000
        move.l #$e00000,a1                      ;Clock,SCSI,Motherboard resources,Chip Registers
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap                             ;add the CIAs
        beq .fail

        sub.l a0,a0
        move.l #$1000,a1
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap                             ;this is CHIP, but definitely available even though not in the memory list
        beq .fail

        move.l mulib_SysBase(a6),a6
        lea CardName,a1
        jsr OpenResource(a6)                    ;card.resource available?
        move.l a5,a6
        tst.l d0
        beq.s .nocard

        move.l #$600000,a0                      ;base of the card resource
        move.l #$a40002,a1                      ;is this right?
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap
        beq.s .fail

.nocard:
        move.l mulib_SysBase(a6),a6
        lea CDStrapName,a1
        jsr FindResident(a6)                    ;cd available?
        move.l a5,a6
        tst.l d0
        beq.s .nocd

        move.l #$e00000,a0                      ;base of the cd ROM
        move.l #$e80000,a1                      ;is this right?
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap
        beq.s .fail

        move.l #$b80000,a0                      ;ditto
        move.l #$bc0000,a1
        move.l #(1<<mapp_cacheinhibit),d0       ;cache inhibited, precise exception model
        bsr _AddMap
        beq.s .fail
.nocd:
        move.l mulib_SysBase(a6),a0
        move.l $a(a0),d0                        ;get name
        clr.w d0
        moveq #$20,d1
        swap d0
        cmp.l d1,d0                             ;is this name in 20K up ?
        bne.s .noexec

        move.l #$200000,a0
        move.l #$280000,a1                      ;is this a mapped ROM image
        move.l #(1<<mapp_writeprotected),d0     ;status
        bsr _AddMap
        beq.s .fail
.noexec:
        moveq #1,d0
.fail:
        loadregs
        rts

_AddMap:
        reserve map_len
        move.l d0,map_properties(a7)
        move.l a0,d0                            ;get lower
        and.l mulib_PageMask(a6),d0
        clr.l map_userptr(a7)
        move.l d0,map_lower(a7)
        move.l a1,d0
        subq.l #1,d0
        add.l mulib_PageSize(a6),d0
        clr.l map_flags(a7)
        and.l mulib_PageMask(a6),d0
        moveq #-1,d1
        subq.l #1,d0
        move.l d0,map_higher(a7)
        move.l d1,map_mask(a7)                  ;mask in all bits
        move.l a2,a0
        move.l a7,a1
        bsr DefineMapping
        restore
        rts
;ENDFOLD
;FOLD AddTransparentTranslation
;*************************************************
;** AddTransparentTranslation                   **
;**                                             **
;** Modify the context *a0 according to the     **
;** address and mask in a1                      **
;** Enable the properties d0, mask in d1        **
;** Context in a0                               **
;*************************************************
        xdef AddTransparentTranslation
AddTransparentTranslation:
        saveregs d2-d6/a2
        defvar
         auto.b att_map,map_len
        endvar

        move.l a1,d2                    ;this is the mask
        move.l d0,map_Properties(a7)
        move.l a1,d3
        move.l d1,map_Mask(a7)
        not.b d2
        lsr.l #8,d3                     ;this is the address
        clr.l map_UserPtr(a7)
        moveq #0,d4
        move.l #$01000000,d6            ;increment
        move.l a0,a2                    ;keep context
        moveq #0,d5
        do
         move.w d4,d0                   ;get the address
         eor.b d3,d0                    ;compare with the address
         and.b d2,d0                    ;and mask
         bne.s .next                    ;does it match ?
         move.l d5,map_lower(a7)
         move.l a2,a0
         move.l d5,map_higher(a7)
         add.l d6,map_higher(a7)
         move.l a7,a1
         subq.l #1,map_higher(a7)
         bsr DefineMapping
         beq.s .exit
.next:
         add.l d6,d5
         addq.b #1,d4
        while.s ne

        moveq #1,d0
.exit:
        freevar
        loadregs
        rts
;ENDFOLD

        section main_data,data
;FOLD Strings
CardName:       dc.b "card.resource",0
CDStrapName:    dc.b "cdstrap",0
;ENDFOLD
;
