*--------------------------------------------------------------------*
*	Startup code to create reentrant program files using the
*	Manx Aztec 'C' compiler (any version).
*
*	Created by:	Olaf Barthel, MXM
*			Brabeckstrasse 35
*			D-3000 Hannover 71
*
*			Federal Republic of Germany
*
*
*			Z-Net: O.BARTHEL@A-Link-H
*			Fido:  Olaf Barthel@2:247/200
*			Sub:   olsen@veeble.uucp
*			UUCP:  olsen%veeble@horga.as.sub.org
*
*	Copyright (C) 1990 by MXM, all rights reserved.
*
*--------------------------------------------------------------------*
*
*	This startup code permits to create 100% reentrant programs.
*	Neither is arp.library required, nor is the program to be
*	terminated via exit() (such as with Lattice/SAS 'C').
*
*	The following conditions must be met for the program to
*	work properly:
*
*	1) The program MUST not use the large code/large data model.
*
*	2) The program MUST not use scatter loading or overlays.
*
*	Since the entire data segment, containing initialized
*	and uninitialized data (bss), is cloned both data types
*	will reside in the same type of memory (chip/public).
*	This may not be a problem since it already is the default
*	with the small memory/small data model.
*
*	Important! Your program MUST not perform a segment split
*	           using the detach.o?? module or its segment list
*	           will be deallocated. Should this happen the next
*	           call to your resident program will crash your
*	           Amiga!
*
*	Should you discover any 'undocumented features' or errors
*	in this program, please DO inform me ASAP!
*
*--------------------------------------------------------------------*
*
*	To create a resident program, use the following sequence:
*
*	AS Resident.asm
*	CC <Programm.c>
*
*	LN <Programm.o> Resident.o -Lc
*
*--------------------------------------------------------------------*

	include	"exec/types.i"
	include	"exec/tasks.i"
	include	"exec/alerts.i"
	include	"exec/memory.i"
	include	"exec/execbase.i"
	include	"libraries/dosextens.i"

*--------------------------------------------------------------------*
*
*	Global macro definitions (doesn't use asmsupp.i).
*
*--------------------------------------------------------------------*

CALL	macro
	xref	_LVO\1
	jsr	_LVO\1(a6)
	endm

AbsExecBase	equ	(4).w

*--------------------------------------------------------------------*
*
*	Global static and shared data.
*
*--------------------------------------------------------------------*

	dseg

DosName:
	dc.b	'dos.library',0			; Name of dos.library.

MemName:
	dc.b	'MXM',0				; Identification of the
						; cloned data segment.

	public	__H1_org,__H1_end,__H2_org,__H2_end
	public	__savsp,_SysBase,_DOSBase

*--------------------------------------------------------------------*

	cseg

	entry	.begin

	public	.begin,_geta4,__main

	mc68881

*--------------------------------------------------------------------*
*
*	Global entry point of the program to follow.
*
*--------------------------------------------------------------------*

.begin
	move.l	sp,a3				; Remember stackpointer.

	far	data
	lea	__H1_org+32766,a4		; Get the data base register.
	near	data

	lea	__H1_end,a1
	lea	__H2_org,a2

	cmp.l	a1,a2				; Small code/small data?
	bne	Error1				; Obviously not!

	movem.l	d0/a0,-(sp)			; Remember command line.

	lea	__H1_org,a1			; Where's the data segment?
	move.l	AbsExecBase,a6			; SysBase.

	CALL	TypeOfMem			; Check memory type

	bset	#MEMB_CLEAR,d0			; We want clear memory.
	bclr	#MEMB_FAST,d0			; Not necessarily fast mem!

*--------------------------------------------------------------------*
*
*	We will create a MemEntry structure right on the stack and
*	will copy the contents of the data segment to the allocated
*	block of memory.
*
*--------------------------------------------------------------------*

	pea	(__H2_end-__H1_org).w		; Length of data segment.
	move.l	d0,-(sp)			; Type of memory.
	pea	(1).w				; Number of entries (1).
	clr.l	-(sp)				; Node...
	clr.l	-(sp)				; Still node...
	clr.l	-(sp)				; Ever still node...

	lea	(sp),a0				; Start of MemEntry.

	CALL	AllocEntry			; Allocate!

	add.w	#24,sp				; Fix the stack.

	move.l	d0,a1				; Did we get an entry?
	beq	Error2				; Fix the stack and quit!

	pea	(a1)				; Put it on the stack.

*--------------------------------------------------------------------*
*
*	To help geta4() to identify the MemEntry we will give it
*	a unique name.
*
*--------------------------------------------------------------------*

	lea	MemName,a0			; Get the node name.

	move.l	a0,LN_NAME(a1)			; Put it in the MemEntry.

	move.l	ThisTask(a6),a0			; Get current task.

	lea	TC_MEMENTRY(a0),a0		; Look for MemList.

	CALL	AddTail				; Add the MemEntry.

	move.l	(sp)+,a1			; Get the MemEntry.
	move.l	ML_SIZE+ME_ADDR(a1),a1		; Remember memory block.

	lea	__H1_org,a0			; Get start of data segment.

	move.l	#((__H1_end-__H1_org)/4)-1,d0	; Remember its length.

*--------------------------------------------------------------------*
*
*	This loop will copy the contents of the data segment to
*	our 'fake' segment.
*
*--------------------------------------------------------------------*

1$	move.l	(a0)+,(a1)+			; Copy data.
	dbra	d0,1$

*--------------------------------------------------------------------*
*
*	Do the last setups for the main program.
*
*--------------------------------------------------------------------*

start	bsr	_geta4				; Load the base register.
	move.l	a3,__savsp			; Set the original stack
						; pointer.
	lea	DosName,a1			; Name of dos.library.
	moveq	#0,d0				; No special version.
	CALL	OpenLibrary			; Open the tin...
	move.l	d0,_DOSBase			; Successful?
	bne	2$				; Start the main program.

	move.l	#AG_OpenLib!AO_DOSLib,d7	; Blast!
	lea	ThisTask(a6),a5			; Current task.
	CALL	Alert				; Call Mr.Guru.
	bra	Error2				; Finish it.

2$	move.l	a6,_SysBase			; Set SysBase.

	andi.w	#AFF_68881,AttnFlags(a6)	; FPU present?
	beq	3$

	lea	Restore,a5
	CALL	Supervisor			; Reset FPU.

3$	jsr	__main				; Call main routine.
	addq.l	#8,sp				; Fix the stack.

*--------------------------------------------------------------------*
*
*	If __main simply 'drops through' dos.library won't have been
*	closed. To keep the counts even, we will test if DOS is still
*	open and close it if necessary.
*
*--------------------------------------------------------------------*

	move.l	_DOSBase,a1
	move.l	a1,d0				; dos.library still open?
	beq	4$				; Don't think so.

	move.l	AbsExecBase,a6			; SysBase.
	CALL	CloseLibrary			; Close the library.

4$	rts					; Back to shell.

*--------------------------------------------------------------------*

Error2:	addq.l	#8,sp				; Fix the stack.

	move.l	AbsExecBase,a6			; SysBase.

*--------------------------------------------------------------------*
*
*	This part handles early startup failure. If this program was
*	started from Workbench the startup message will probably
*	be roaming around. We will intercept it and send it back
*	to the caller.
*
*--------------------------------------------------------------------*

	move.l	ThisTask(a6),a0			; Get current task.
	tst.l	pr_CLI(a0)			; Started from Workbench?
	bne	Error1

	lea	pr_MsgPort(a0),a0		; Remember our MsgPort.
	move.l	a0,a3

	CALL	WaitPort			; Wait for WBenchStartup.

	move.l	a3,a0				; Return MsgPort.

	CALL	GetMsg				; Pick up WBenchMsg.

	move.l	d0,a3				; Remember Message.

	CALL	Forbid				; Turn off multitasking.

	move.l	a3,a1				; Return the message.

	CALL	ReplyMsg			; And reply it.

Error1:	moveq	#-1,d0				; Failed!
	rts

*--------------------------------------------------------------------*
*
*	This subroutine will reset the FPU (if any)!
*
*--------------------------------------------------------------------*

Restore:
	clr.l	-(sp)
	frestore (sp)+				; Reset the FPU.
	rte

*--------------------------------------------------------------------*
*
*	One of the most important routines of this file. Sets
*	the global data segment pointer to a defined value.
*
*--------------------------------------------------------------------*

_geta4:
	movem.l	d0-d1/a0-a1/a6,-(sp)		; Save some registers.
	move.l	AbsExecBase,a6			; SysBase.
	move.l	ThisTask(a6),a0			; Get current task.
	lea	TC_MEMENTRY(a0),a0		; Remember MemList.
	lea	MemName,a1			; Get name of MemEntry.

	CALL	FindName			; Scan the list...

	move.l	d0,a1
	move.l	ML_SIZE+ME_ADDR(a1),a4		; Start of data segment.
	lea	32766(a4),a4			; Add the offset.

	movem.l	(sp)+,d0-d1/a0-a1/a6		; Restore all registers.
	rts

*--------------------------------------------------------------------*

	end
