;--------------------------------------------------
;	lk V1.01 Overlay code.
;	$VER: overlay.s 1.00 (10.07.94)
;	Written by Alexis WILKE (c) 1994.
;
;	This code is given as an example of overlay
;	handler. This consist into an AmigaDOS
;	loader which will load overlayed hunks when
;	necessary. To rescue the allocated memory
;	when a hunk is no more needed, a call to
;	the 'discard' function can be supplied.
;
;	The functions in C would be describ as
;	follow:
;
;	extern	void	*overlay(long);
;	void *overlay(hunk)
;	short	hunk;
;
;	This function tries to load the given hunk.
;	If the hunk number is -1, all hunks will
;	be loaded.
;	A call to this function is always necessary
;	to ensure the presence of the hunk.
;	The returned pointer is a pointer at the
;	beginning of the hunk.
;
;	extern	short	discard(long);
;	short discard(hunk)
;	short	hunk;
;
;	This function tries to discard (free from
;	memory) the given hunk. An access counter
;	is keept for each hunk to know about the
;	number of accesses to it.
;	It returns TRUE (1) when the hunk is freed
;	FALSE (0) otherwise.
;
;	lk auto-generate some code to call those
;	two functions. This code is:
;
;func_name:
;	Jsr	_call
;	Dc.W	<hunk_number>
;	Dc.L	<func_offset>
;--------------------------------------------------

	INCDIR	'INCLUDE:'
	INCDIR	'INCLUDE:INCLUDE.STRIP/'
	INCLUDE	'EXEC/memory.i'
	INCLUDE	'DOS/dos.i'
	INCLUDE	'DOS/doshunks.i'
	INCLUDE	'sw.i'		;include function offsets

	XDEF	_ovl_load
	XDEF	_ovl_release
	XDEF	_ovl_dispatch
	XDEF	_ovl_freehook
	XDEF	_ovl_call

	XREF	_ovl_root

ON	=	0
OFF	=	-1

AUTODISPATCH	=	OFF		;Dispatch hunk when we quit them
MULTITASK	=	OFF		;Enable an overlaid file with multiple tasks



	SECTION	OVERLAYENTRY,CODE
;--------------------------------------------------
start:
	Bra.W	_init
					;This is for system tables
	Dc.L	$0000ABCD		;Magic long
fh
	Dc.L	0			;File handle
offset
	Dc.L	0			;Pointer to the hunk offsets
	Dc.L	0			;?
	Dc.L	0			;Functions table

dbase		Dc.L	0
exitprt		Dc.L	0
hunklist	Dc.L	0
hunkcount	Dc.L	0
userfunc	Dc.L	0		;User function called on a memory/disk error
stack		Dc.L	0
buffer		Ds.L	5
dname
	Dc.B	"dos.library",0
	EVEN
_init
	MoveM.L	D0-D7/A0-A6,-(A7)
	MoveA.L	(4).W,A6
	Lea	dname(PC),A1
	Move.L	(8+7)*4(A7),exitprt-dname(A1)		;Get normal exit pointer
	Lea	_free(PC),A0				;Save our exit pointer
	Move.L	A0,(8+7)*4(A7)
	Jsr	_OldOpenLibrary(A6)
	Lea	dbase(PC),A1
	Move.L	D0,(A1)
	Bne.B	.ok
.fails
	MoveQ	#$00,D0
	Bra	_error
.ok
	MoveA.L	offset(PC),A1
	Move.L	(A1)+,D0
	Lea	hunkcount(PC),A0
	Move.L	D0,(A0)
	Lsl.L	#$03,D0
	AddA.L	D0,A1
	Lea	hunklist(PC),A0
	Move.L	A1,(A0)			;Save pointer on the second table

_main
	MoveM.L	(A7)+,D0-D7/A0-A6
	Jmp	_ovl_root		;Go to your program
_error
	AddA.W	#(8+7)*4,A7
_free
	Move.L	exitprt(PC),-(A7)
	MoveM.L	D0-D7/A0-A6,-(A7)
	MoveA.L	(4).W,A6
	Move.L	hunklist(PC),D0
	Beq.B	.nohunk
	MoveA.L	offset(PC),A0
	Move.L	(A0),D2
	MoveA.L	D0,A2
.free
	AddQ.L	#$04,A2			;Skip hunk number
	Move.L	(A2)+,D0
	Beq.B	.skip
	Clr.L	-4(A2)			;Ensure the system does not take care
	MoveA.L	D0,A3			; of that...
.next
	MoveA.L	A3,A1
	MoveA.L	4(A3),A3
	Move.L	(A1),D0
	Jsr	_FreeMem(A6)
	Move.L	A3,D0
	Bne.B	.next
.skip
	SubQ.L	#$01,D2
	Bne.B	.free
.nohunk
	Move.L	dbase(PC),D0
	Beq.B	.nodos
	MoveA.L	D0,A1
	Jsr	_CloseLibrary(A6)
.nodos
	MoveM.L	(A7)+,D0-D7/A0-A6
	Rts


;--------------------------------------------------
;	Load an overlay hunk.
;Inputs:
;	D0 as the hunk number or -1 for all hunks
;Return:
;	D0 with the pointer on the first hunk of the specified unit
;	or
;	TRUE when all hunks was loaded correctly
;--------------------------------------------------
_ovl_load
	Move.L	4(A7),D0
@ovl_load
	MoveM.L	D2-D6/A2-A4/A6,-(A7)
	Lea	stack(PC),A0
	Move.L	A7,(A0)
	IFEQ	MULTITASK
	MoveA.L	(4).W,A6
	Jsr	_Forbid(A6)			;<- this should be a semaphore
	ENDC
	Move.L	D0,D4
	AddQ.L	#$01,D4
	Bne.B	.specific
.all
	Move.L	D4,D0
	Bsr.B	@ovl_load
	Tst.L	D0
	Beq	.error
	Cmp.L	hunkcount(PC),D4
	Bcs.B	.all
	MoveQ	#DOSTRUE,D0
	Bra	.quit
.specific
	SubQ.L	#$01,D4
	Cmp.L	hunkcount(PC),D4
	Bcc	.error
	Lsl.L	#$03,D4
	MoveA.L	hunklist(PC),A1
	Move.L	4(A1,D4.L),D0			;Already defined?
	Bne	.access

	MoveA.L	dbase(PC),A6
	Move.L	fh(PC),D1
	MoveA.L	offset(PC),A1
	Move.L	4(A1,D4.L),D2			;Get the offset in file
	MoveQ	#OFFSET_BEGINING,D3
	Jsr	_Seek(A6)			;Go to the header
	Jsr	_IoErr(A6)
	Tst.L	D0
	Beq.B	.posok
	Bsr	.doserror
	Bra	.error
.posok
	Move.L	#5*4,D3
	Bsr	.readbuffer
	Bne	.error
	Lea	buffer(PC),A0
	Cmp.L	#HUNK_HEADER,(A0)+		;Header?
	Bne	.error
	Tst.L	(A0)+				;No name allowed
	Bne	.error
	AddQ.L	#$04,A0				;Skip that unknown hunk
	Move.L	(A0)+,D1
	Move.L	(A0),D0
	MoveA.L	hunklist(PC),A0
	Cmp.W	(A0,D4.L),D1			;Same hunk?
	Bne	.error

	Sub.L	D1,D0
	AddQ.L	#$01,D0				;Number of hunk to load
	Lsl.L	#$03,D0				;Size+Attributes
	Move.L	D0,D3
	AddQ.L	#$04,D0
	MoveQ	#$00,D1
	MoveA.L	(4).W,A6
	Jsr	_AllocMem(A6)

	Tst.L	D0
	Beq	.error
	MoveA.L	D0,A2
	Move.L	D3,(A2)+
	MoveA.L	A2,A4

	Move.L	A2,D2
	Bsr	.read
	Bne	.free

	MoveA.L	(4).W,A6
	MoveA.L	A2,A3
	MoveQ	#$00,D2				;Seek()
	Lsr.L	#$03,D3
	SubQ.L	#$01,D3
.alloc
	Move.L	(A3)+,D0
	MoveQ	#$03<<1,D1			;Memory attributs
	Rol.L	#$03,D0
	And.L	D0,D1
	Ror.L	#$01,D0
	AndI.L	#$00FFFFFF<<2,D0		;Clear any flag
	Move.W	0$(PC,D1.W),D1
	Jmp	0$(PC,D1.W)
0$
	Dc.W	.public-0$
	Dc.W	.chip-0$
	Dc.W	.fast-0$
	Dc.W	.attributs-0$
.chip
	Move.L	#MEMF_CHIP,D1
	Bra.B	.seek
.fast
	Move.L	#MEMF_FAST,D1
	Bra.B	.seek
.attributs
	Move.L	(A3)+,D1			;Get user attributs
	Bra.B	.go
.public
	MoveQ	#$00,D1
.seek
	SubQ.L	#$04,D2				;Seek()
.go
	AndI.L	#~MEMF_CLEAR,D1
	AddQ.L	#$08,D0
	Move.L	D0,D5
	Jsr	_AllocMem(A6)			;Allocates hunks
	Tst.L	D0
	Beq	.free
	Move.L	D0,(A4)+
	MoveA.L	D0,A0
	Move.L	D5,(A0)
	Dbf	D3,.alloc

	MoveA.L	A4,A3				;Link each allocated hunk
	MoveA.L	-(A3),A1
	Clr.L	4(A1)
	Bra.B	.entry
.link
	Move.L	A1,D0
	MoveA.L	-(A3),A1
	Move.L	D0,4(A1)
.entry
	CmpA.L	A2,A3
	Bhi.B	.link

	MoveA.L	dbase(PC),A6
	Move.L	fh(PC),D1
	MoveQ	#OFFSET_CURRENT,D3
	Jsr	_Seek(A6)
	Jsr	_IoErr(A6)
	Tst.L	D0
	Bne	.dosfree

	MoveA.L	A2,A3
.loading
	MoveQ	#$08,D3
	Bsr	.readbuffer
	Bne	.free
	Move.L	buffer(PC),D0
	Cmp.L	#HUNK_CODE,D0			;Only CODE/DATA/BSS types are ok
	Beq.B	.typeok
	Cmp.L	#HUNK_DATA,D0
	Beq.B	.typeok
	Cmp.L	#HUNK_BSS,D0
	Bne	.free
	MoveA.L	(A3),A0
	Move.L	(A0),D0
	Beq.B	.load
	Bra.B	.clr
.typeok
	Move.L	(A3),D2
	AddQ.L	#$08,D2
	Move.L	buffer+4(PC),D3			;Size to be read
	Lsl.L	#$02,D3
	Bsr	.read
	Bne	.free
	MoveA.L	(A3),A0
	Move.L	(A0),D0				;Same size?
	SubQ.L	#$08,D0
	Sub.L	D3,D0
	Beq.B	.load
;	Lsl.L	#$02,D3
	AddA.L	D3,A0				:No, then clear the end...
.clr
	AddQ.L	#$08,A0
	SubQ.L	#$08,D0
.clear
	Clr.L	(A0)+				;Clear the BSS and XCODE/XDATA hunks
	SubQ.L	#$04,D0
	Bne.B	.clear
.load
	Bsr	.readlong
	Bne	.free
	Move.L	buffer(PC),D0
	CmpI.L	#HUNK_END,D0
	Beq	.end
	CmpI.L	#HUNK_ABSRELOC32,D0
	Bne.B	.noreloc
.nextreloc
	Bsr	.readlong			;Get number of reloc
	Bne	.free
	Move.L	buffer(PC),D5
	Beq	.load
	Bsr	.readlong			;Get hunk number
	Bne	.free
	Move.L	buffer(PC),D0
	Bsr	.gethunk
	Beq	.free
.reloc
	Bsr	.readlong			;Reloc...
	Move.L	buffer(PC),D0
	AddQ.L	#$08,D0
	Bcs	.free
	MoveA.L	(A3),A0
	Cmp.L	(A0),D0				;Overflow?
	Bcc	.free
	Add.L	D6,(A0,D0.L)
	SubQ.W	#$01,D5
	Bne	.reloc
	Bra.B	.nextreloc
.noreloc
	CmpI.L	#HUNK_DREL32,D0			;V37 bug!
	Beq.B	.short
	CmpI.L	#HUNK_RELOC32SHORT,D0
	Bne.B	.noshort
	MoveQ	#$00,D5
.short
	MoveQ	#$02,D3
	Bsr	.readbuffer			;Read first word (Size)
	Bne	.free
	Move.W	buffer(PC),D5
	Beq	.check
	BTst.L	#$00,D5
	Beq.B	.even
	BChg.L	#$1F,D5
.even
	MoveQ	#$02,D3
	Bsr	.readbuffer			;Read second word (Hunk)
	MoveQ	#$00,D0
	Move.W	buffer(PC),D0
	Bsr	.gethunk
	Beq	.free
.relocshort
	MoveQ	#$02,D3
	Bsr	.readbuffer			;Read second word (Hunk)
	MoveQ	#$00,D0
	Move.W	buffer(PC),D0
	Move.L	(A3),A0
	Cmp.L	(A0),D0				;Overflow?
	Bcc	.free
	Add.L	D6,8(A0,D0.L)
	SubQ.W	#$01,D5
	Bne.B	.relocshort
	Bra.B	.short
.check
	Tst.L	D5
	Bmi	.load
	Move.L	fh(PC),D1
	MoveQ	#$02,D2
	MoveQ	#OFFSET_CURRENT,D3
	Jsr	_Seek(A6)
	Jsr	_IoErr(A6)
	Tst.L	D0
	Bne	.dosfree
	Bra	.load
.noshort
	CmpI.L	#HUNK_SYMBOL,D0
	Bne.B	.nosymbol
.abs
	Bsr	.readlong
	Bne	.free
	Move.L	buffer(PC),D2			;End of table?
	Beq.B	.load
	Move.L	fh(PC),D1
	AddQ.L	#$01,D2				;Skip value
	Lsl.L	#$02,D2
	MoveQ	#OFFSET_CURRENT,D3
	Jsr	_Seek(A6)			;Skip name
	Jsr	_IoErr(A6)
	Tst.L	D0
	Beq	.abs
	Bra	.dosfree
.nosymbol
	CmpI.L	#HUNK_DEBUG,D0
	Bne.B	.free
	Bsr	.readlong
	Bne	.free

	Move.L	fh(PC),D1
	Move.L	buffer(PC),D2
	Lsl.L	#$02,D2
	MoveQ	#OFFSET_CURRENT,D3
	Jsr	_Seek(A6)
	Jsr	_IoErr(A6)
	Tst.L	D0
	Beq	.load
.dosfree
	Bsr	.doserror
	Bra	.free
.end
	AddQ.L	#$04,A3
	CmpA.L	A4,A3
	Bcs.B	.loading
	Bsr	.readlong
	Bne	.free
	Move.L	buffer(PC),D0
	CmpI.L	#HUNK_BREAK,D0
	Bne	.error

	Move.L	hunklist(PC),A1
	Move.L	(A2),D2
	Move.L	D2,4(A1,D4.L)

	MoveA.L	(4).W,A6
	MoveA.L	A2,A1
	Move.L	-(A1),D0
	AddQ.L	#$04,D0
	Jsr	_FreeMem(A6)

	Move.L	D2,D0				;Pointer on the first hunk
.access
	AddQ.L	#$08,D0
	MoveA.L	hunklist(PC),A1
	AddQ.W	#$0001,2(A1,D4.L)
	Bne.B	.quit
	SubQ.W	#$0001,2(A1,D4.L)
.quit
	Bra.B	.exit
.free
	Bsr.B	.freetable
.error
	MoveQ	#$00,D0
.exit
	IFEQ	MULTITASK
	MoveA.L	(4).W,A6
	Jsr	_Permit(A6)			;<- this should be a semaphore
	ENDC
	MoveA.L	stack(PC),A7
	MoveM.L	(A7)+,D2-D6/A2-A4/A6
	Rts
.readlong
	MoveQ	#$04,D3
.readbuffer
	Lea	buffer(PC),A0
	Move.L	A0,D2
.read
	MoveA.L	dbase(PC),A6
	Move.L	fh(PC),D1
	Jsr	_Read(A6)			;Get debug hunk size
	Cmp.L	D0,D3
	Beq.B	.nodoserr
.doserror
	Move.L	userfunc(PC),D0			;User function?
	Beq.B	.nouf
	MoveA.L	D0,A0
	MoveQ	#-1,D0
	Move.L	D0,-(A7)
	Jsr	(A0)
	AddQ.L	#$04,A7
.nouf
	MoveQ	#-1,D0
.nodoserr
	Rts
.freetable
	MoveA.L	(4).W,A6
.freemore
	CmpA.L	A2,A4
	Beq.B	.freed
	MoveA.L	-(A4),A1
	Move.L	(A1),D0
	Jsr	_FreeMem(A6)
	Bra.B	.freemore
.freed
	MoveA.L	A2,A1
	Move.L	-(A1),D0
	AddQ.L	#$04,D0
	Jmp	_FreeMem(A6)
.gethunk
	Cmp.L	buffer+3*4(PC),D0
	Bcc.B	.here
	Lea	start-8(PC),A0
.seekhunk
	SubQ.L	#$01,D0
	Bcs.B	.done
	Move.L	4(A0),D1
	Beq.B	.badhunk
	Lsl.L	#$02,D1
	SubQ.L	#$04,D1
	MoveA.L	D1,A0
	Bra.B	.seekhunk
.done
	Move.L	A0,D6
	AddQ.L	#$08,D6
	Rts
.here
	Cmp.L	buffer+4*4(PC),D0
	Bhi.B	.badhunk
	Sub.L	buffer+3*4(PC),D0
	Lsl.L	#$02,D0
	Move.L	(A2,D0.L),D6
	AddQ.L	#$08,D6
	Rts
.badhunk
	MoveQ	#$00,D6
	Rts


;--------------------------------------------------
;	Try to free an overlay hunk.
;Inputs:
;	the hunk number or -1 for all hunks
;Return:
;	TRUE when the object was discarded
;--------------------------------------------------
_ovl_release
	Move.L	4(A7),D0
@ovl_release
	IFEQ	MULTITASK
	MoveM.L	D4/A6,-(A7)
	Jsr	_Forbid(A6)
	ELSE
	Move.L	D4,-(A7)
	ENDC
	Move.L	D0,D4
	AddQ.L	#$01,D4
	Bne.B	.specific
.all
	Move.L	D4,D0
	Bsr.B	@ovl_release
	AddQ.L	#$01,D4
	Cmp.L	hunkcount(PC),D4
	Bcs.B	.all
	Bra	.exit
.specific
	SubQ.L	#$01,D4
	Cmp.L	hunkcount(PC),D4		;Is hunk number valid?
	Bcc	.error
	Lsl.L	#$03,D4
	MoveA.L	hunklist(PC),A1
	Lea	2(A1,D4.L),A1
	Move.W	(A1),D0				;Accessed?
	Beq.B	.quit
	AddQ.W	#$01,D0				;Locked forever?
	Beq.B	.quit
	SubQ.W	#$01,(A1)
.quit
	MoveQ	#DOSTRUE,D0
	Bra.B	.exit
.error
	MoveQ	#DOSFALSE,D0
.exit
	IFEQ	MULTITASK
	Jsr	_Permit(A6)
	MoveM.L	(A7)+,D4/A6
	ELSE
	Move.L	(A7)+,D4
	ENDC
	Rts


;--------------------------------------------------
;	Try to free an overlay hunk.
;Inputs:
;	the hunk number or -1 for all hunks
;Return:
;	TRUE when the object was discarded
;--------------------------------------------------
_ovl_dispatch
	Move.L	4(A7),D0
@ovl_dispatch
	MoveM.L	D4/A6,-(A7)
	MoveA.L	(4).W,A6
	IFEQ	MULTITASK
	Jsr	_Forbid(A6)
	ENDC
	Move.L	D0,D4
	AddQ.L	#$01,D4
	Bne.B	.specific
.all
	Move.L	D4,D0
	Bsr.B	@ovl_dispatch
	AddQ.L	#$01,D4
	Cmp.L	hunkcount(PC),D4
	Bcs.B	.all
	Bra	.exit
.specific
	SubQ.L	#$01,D4
	Cmp.L	hunkcount(PC),D4		;Is hunk number valid?
	Bcc	.error
	Lsl.L	#$03,D4
	MoveA.L	hunklist(PC),A1
	Lea	2(A1,D4.L),A1
	Tst.W	(A1)+				;Accessed?
	Bne.B	.error
	Move.L	(A1),D4				;Still in memory
	Beq.B	.quit
	Clr.L	(A1)				;Freed!
.next
	MoveA.L	D4,A1
	Move.L	(A1),D0
	Move.L	4(A1),D4			;Get next before to free hunk
	Jsr	_FreeMem(A6)
	Tst.L	D4
	Bne.B	.next
.quit
	MoveQ	#DOSTRUE,D0
	Bra.B	.exit
.error
	MoveQ	#DOSFALSE,D0
.exit
	IFEQ	MULTITASK
	Jsr	_Permit(A6)
	ENDC
	MoveM.L	(A7)+,D4/A6
	Rts


;--------------------------------------------------
;Inputs:
;	D0 as the function pointer
;--------------------------------------------------
_ovl_freehook
	Move.L	4(A7),D0
@ovl_freehook
	Lea	userfunc(PC),A0
	Move.L	D0,(A0)
	Rts


;--------------------------------------------------
;	This function receive a structure pointer
;	as a parameter. That structure is:
;		struct CALL {
;			short	hunk_number;
;			long	func_offset;
;		}
;Inputs:
;	(A7) as the pointer on the hunk_number/func_offset
;--------------------------------------------------
_ovl_call
	Move.W	CCR,-(A7)
	MoveM.L	D0-D2/A0-A1,-(A7)
	MoveA.L	5*4+2(A7),A0
	MoveQ	#$00,D0
	Move.W	(A0),D0				;Get hunk number
	Bsr	@ovl_load
	Tst.L	D0
	Bne.B	.call
	MoveQ	#-1,D0
	Bsr	@ovl_dispatch			;Try to free all hunks in memory
	MoveQ	#$00,D2				; and unused.
.next
	MoveA.L	5*4+2(A7),A0
	MoveQ	#$00,D0
	Move.W	(A0),D0				;Get hunk number
	Bsr	@ovl_load
	Tst.L	D0
	Bne.B	.call
	Move.L	userfunc(PC),D0
	Beq.B	.fails
	MoveA.L	D0,A0
	Move.L	D2,-(A7)
	Move.L	D2,D0
	Jsr	(A0)
	AddQ.L	#$04,A7
	AddQ.L	#$01,D2
	Bra.B	.next
.call
	MoveA.L	5*4+2(A7),A0
	Add.L	2(A0),D0
	Pea	.back(PC)
	Move.L	D0,-(A7)			;Save the function pointer on
	MoveM.L	8(A7),D0-D2/A0-A1		; the stack
	Rts
.back
	Move.W	CCR,5*4(A7)
	MoveM.L	D0-D2/A0-A1,(A7)
	MoveA.L	5*4+2(A7),A0
	MoveQ	#$00,D0
	Move.W	(A0),D0
	Bsr	@ovl_release

	IFEQ	AUTODISPATCH
	MoveA.L	5*4+2(A7),A0			;Dispatch automatically?
	MoveQ	#$00,D0
	Move.W	(A0),D0
	Bsr	@ovl_dispatch
	ENDC
.fails
	Move.W	4*5(A7),4*6(A7)
	MoveM.L	(A7)+,D0-D2/A0-A1
	AddQ.L	#$04,A7
	Rtr


