********************************************************************
*
*                                  MemMan
*                            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 wish 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.)
*
********************************************************************
*
* 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...

	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 wonderful? :-)
* This makes each AllocMem() call slightly faster and should be OK if we
* are careful of the caches.
codest:
	push.l	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.l	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.l	d0-d1/a0-a1		; Some nasty apps rely on d1/a0/a1...
	PERMIT
	pop.l	d0-d1/a0-a1/a2-a3
	addq.l	#8,sp			; Pop AllocMem args off stack
	rts

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

codefin:

	; Continuation of MemManSem structure
	STRUCT	mms_Code_def2,(codefin-codest)
	LABEL	mms_SIZEOF

*** Initialize MemMan - patches AllocMem()
* Returns:
* d0 = Nonzero if successful, zero if failed
MMInit:
_MMInit:
	push.l	a5/a6
	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
	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 reactive 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:
	pop.l	a5/a6
	rts


*** Uninstall our application from the memory manager
MMFinish:
_MMFinish:
	push.l	a5/a6
	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:
	pop.l	a5/a6
	rts


*** Add an MMNode (if it wasn't already on the MMList)
* a1 = Pointer to MMNode
MMAddNode:
_MMAddNode:
	tst.b	mmn_Linked(a1)	; Don't add a node twice
	bnz.s	9$
	
	st.b	mmn_Linked(a1)	; Mark it as added
	
	push.l	a6		; Add it to the global list in prioritized order
	move.l	4,a6
	FORBID
	move.l	mmlist(pc),a0
	jsr	_LVOEnqueue(a6)
	PERMIT
	pop.l	a6
	
9$	rts


*** Remove an MMNode (only if it was on the MMList)
* a1 = Pointer to MMNode
MMRemNode:
_MMRemNode:
	tst.b	mmn_Linked(a1)	; Don't remove unless it was added
	bz.s	9$
	
	clr.b	mmn_Linked(a1)	; Mark it as not added
	
	push.l	a6		; Remove it from the public MMList
	move.l	4,a6
	FORBID
	REMOVE
	PERMIT
	pop.l	a6
	
9$	rts

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

	end
