********************************************************************************
*
*                                  MemMan
*                                Version 2.0
*                            Low-Memory manager
*                       Copyright (C) 1991 Bryan Ford
*
*
* This source code may be freely distributed as long as it is complete (see
* MemMan.doc for the required files), all of the files are unmodified, and no
* charge is made for such distribution other than a small fee to cover the
* cost of copying (no profit may be made through distribution of this
* program).  You may modify this code for your own personal use, but you may
* not distribute modified versions.  (Just send your improvements back to me
* - I'll incorporate them in the master, of course giving credit to you, and
* that way we won't have hundreds of different versions floating around.)
*
* You may use the object code derived from this (or your own modified version
* of this) source code in your own public domain, freeware, or shareware
* programs (programs that are distributed on similar terms to this source
* code), as long as you mention the use of MemMan and its author (me)
* somewhere in your documentation.  You do not need to obtain any kind of
* license in this case.
*
* If you want to distribute MemMan (or any modified variation of it), in
* either source or object form, with a commercial package, you must obtain my
* written permission.  (Don't worry, I'm not starving at this point - a
* license will be very cheap, most likely consisting simply of a copy of the
* program you're using it in.)
*
********************************************************************************
*
* Thanks to Michael Sinz of Commodore for his valuable suggestions for improving
* this code and making it more compatible with future versions of the OS.
*
* Also, special thanks to David Le Blanc for the library version and the test
* program.
*
********************************************************************************
*
* Assemble with A68k version >2.71.
*

        include "exec/types.i"
        include "exec/lists.i"
        include "exec/ables.i"
        include "exec/memory.i"
        include "exec/libraries.i"
        include "exec/semaphores.i"
        include "exec/funcdef.i"
        include "exec/exec_lib.i"
        include "bry/macros.i"
        include "bry/memman.i"

 STRUCTURE      MemManSem,SS_SIZE       ; SignalSemaphore
        UWORD   mms_AppCount            ; Number of apps using MemMan now
        STRUCT  mms_MMList,MLH_SIZE     ; List we link MMNodes onto
        LABEL   mms_Code                ; Copied MemMan library code
        ; Structure is continued farther down... (hehe)

        code    text

        xdef    MMInit,MMFinish,MMAddNode,MMRemNode
        xdef    _MMInit,_MMFinish,_MMAddNode,_MMRemNode

* This code is copied into the global MemManSem structure.
* The first six bytes are overwritten with a JMP instruction
* whenever MemMan is not in use.
* Immediately after that is the old AllocMem vector
* (last part of the JSR instruction).
* Yup, self-modifying code - isn't it beautiful? :-)
* This makes each AllocMem() call slightly faster and should be fine if we
* are very careful of caching (which we are).  Since the only self-modifying
* code we use is in MEMF_PUBLIC memory, there should be no virtual or protected
* memory problems either.
codest:
        push    d0/d1                   ; Save registers (4 bytes of code)

jsrinst:
        jsr     $12345678               ; Call regular AllocMem() (2 + 4 bytes)
        tst.l   d0
        bz.s    \failed

        addq.l  #8,sp                   ; Succeeded on the first try
        rts

\failed:
        push    a2-a3
        move.l  codest-mms_Code+mms_MMList+LH_HEAD(pc),a2
        move.l  jsrinst+2(pc),a3

        FORBID

\retry:
        move.l  LN_SUCC(a2),d1          ; Traverse the list forwards
        bz.s    \fin                    ; (kill HIGHEST priority nodes first)

        move.l  mmn_GetRidFunc(a2),a1   ; Call the GetRidFunc
        move.l  mmn_GetRidData(a2),a0
        move.l  d1,a2                   ; Find next node BEFORE call
        movem.l 8(sp),d0/d1
        jsr     (a1)

        movem.l 8(sp),d0/d1             ; Try allocating again
        jsr.l   (a3)
        tst.l   d0
        bz.s    \retry

\fin:
        push    d0-d1/a0-a1             ; Some nasty apps rely on d1/a0/a1...
        PERMIT
        pop     d0-d1/a0-a1/a2-a3
        addq.l  #8,sp                   ; Pop AllocMem args off stack
        rts

semname:
        dc.b    "MemMan2",0
        ds.w    0

codefin:

        ; Continuation of MemManSem structure (now that we know the code size)
        STRUCT  mms_Code_def2,(codefin-codest)
        LABEL   mms_SIZEOF

*** Initialize MemMan - patches AllocMem()
* Returns:
* d0 = Nonzero if successful, zero if failed
MMInit:
_MMInit:
        apush
        move.l  4,a6

        lea     semname(pc),a1          ; See if the semaphore already exists
        jsr     _LVOFindSemaphore(a6)
        move.l  d0,semaphore
        bnz     \oldsem

        ; There is the VERY slight chance we could get two MemMan semaphores
        ; at once.  Who cares?  Nothing terrible would happen anyway.
\newsem:
        move.l  #mms_SIZEOF,d0          ; Allocate the public memory block
        move.l  #MEMF_PUBLIC!MEMF_CLEAR,d1
        jsr     _LVOAllocMem(a6)
        move.l  d0,semaphore
        bz      \out
        move.l  d0,a5

        lea     mms_MMList(a5),a0       ; Initialize the data area
        NEWLIST a0
        move.w  #1,mms_AppCount(a5)
        lea     mms_Code+(semname-codest)(a5),a0
        move.l  a0,LN_NAME(a5)

        lea     codest(pc),a0           ; Copy the public code
        lea     mms_Code(a5),a1
        move.l  #codefin-codest,d0
        jsr     _LVOCopyMem(a6)

        FORBID                          ; No AllocMem()s in here please

        move.l  a6,a1                   ; SetFunction AllocMem()
        lea     mms_Code(a5),a0
        move.l  a0,d0
        movea.w #_LVOAllocMem,a0
        jsr     _LVOSetFunction(a6)

        move.l  d0,mms_Code+(jsrinst-codest)+2(a5)      ; Set JSR vector

        cmpi.w  #36,LIB_VERSION(a6)     ; Clear the cache after setting vector
        blo.s   \newsemcc
        jsr     _LVOCacheClearU(a6)
\newsemcc:
        PERMIT

        move.l  a5,a1                   ; Add to system semaphore list
        move.l  a5,a0                   ; BUUUUUUUUUUUUUUUG in 1.3!!!
        jsr     _LVOAddSemaphore(a6)

        bra     \outok

\oldsem:                                ; Semaphore was already in memory
        move.l  d0,a5

        move.l  d0,a0
        jsr     _LVOObtainSemaphore(a6)

        addq.w  #1,mms_AppCount(a5)

        cmp.w   #$4ef9,mms_Code(a5)     ; See if we have to reactivate the code
        bne.s   \codefine

        FORBID                          ; No AllocMem()s while we change code

        move.l  codest(pc),mms_Code(a5) ; Restore first six bytes
        move.w  codest+4(pc),mms_Code+4(a5) ; No more or we'll trash the vector

        cmpi.w  #36,LIB_VERSION(a6)     ; Clear the cache after changing code
        blo.s   \oldsemcc
        jsr     _LVOCacheClearU(a6)
\oldsemcc:

        PERMIT
\codefine:

        move.l  a5,a0
        jsr     _LVOReleaseSemaphore(a6)
\outok:
        lea     mms_MMList(a5),a0
        move.l  a0,mmlist

        moveq   #1,d0
\out:
        apop
        rts


*** Uninstall our application from the memory manager
MMFinish:
_MMFinish:
        apush
        move.l  4,a6

        move.l  semaphore,d0            ; Never successfully initialized?
        bz      \out
        move.l  d0,a5

        move.l  a5,a0
        jsr     _LVOObtainSemaphore(a6)

        subq.w  #1,mms_AppCount(a5)
        bnz.s   \otherapps

        FORBID                          ; No AllocMem()s while we change the code

        move.w  #$4ef9,mms_Code(a5)     ; Quick bounce with a JMP instruction
        move.l  mms_Code+(jsrinst-codest)+2(a5),mms_Code+2(a5)

        cmpi.w  #36,LIB_VERSION(a6)     ; Clear the cache after changing code
        blo.s   \cc
        jsr     _LVOCacheClearU(a6)
\cc:

        PERMIT
\otherapps:

        move.l  a5,a0
        jsr     _LVOReleaseSemaphore(a6)

        clr.l   semaphore               ; Safety

\out:
        apop
        rts


*** Add an MMNode (if it wasn't already on the MMList)
* a1 = Pointer to MMNode
MMAddNode:
_MMAddNode:
        cmp.b   #MMNT_LINKED,LN_TYPE(a1)        ; Don't add a node twice
        beq.s   9$

        mb      #MMNT_LINKED,LN_TYPE(a1)        ; Mark it as added

        push    a6              ; Add it to the global list in prioritized order
        move.l  4,a6
        move.l  mmlist,a0
        FORBID
        jsr     _LVOEnqueue(a6)
        PERMIT
        pop     a6

9$      rts


*** Remove an MMNode (only if it was on the MMList)
* a1 = Pointer to MMNode
MMRemNode:
_MMRemNode:
        cmp.b   #MMNT_LINKED,LN_TYPE(a1)        ; Don't remove unless it was added
        bne.s   9$

        clr.b   LN_TYPE(a1)     ; Mark it as not added

        push    a6              ; Remove it from the public MMList
        move.l  4,a6
        FORBID
        REMOVE
        PERMIT
        pop     a6

9$      rts

        bss     __MERGED

semaphore       ds.l    1               ; Pointer to public semaphore
mmlist          ds.l    1               ; Points to mms_MMList in MemManSem

        end
