/**********************************************************************
* DemoResident
* di Romano Tenca
* Un esempio di programma in C per costruire moduli residenti.
*
* Public Domain (non usare per scopi commerciali senza autorizzazione).
*
* Compilare con Lattice (versioni 4.x o 5.x)
*   lc1 DemoResident.c
*   lc2 -v -y DemoResident.c
*
* Linkare con Blink
*   blink from DemoResident.o to DemoResident SMALLCODE SMALLDATA NODEBUG
*
* Ampiezza Tab : 4
***********************************************************************/

#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/memory.h"
#include "exec/ports.h"
#include "exec/tasks.h"
#include "exec/libraries.h"
#include "exec/Resident.h"
#include "exec/execbase.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

#include "string.h"

#include "proto/exec.h"
#include "proto/dos.h"
#include "proto/intuition.h"

/* FUNZIONI */

void PreparaResident(
	struct Resident	*tagptr,
	UBYTE 			vers,
	BYTE 			pri,
	char 			*nome,
	char 			*id,
	void 			(*init)());

long *PreparaMemList(
	struct MemList	*mlptr,
	short			numentries);

void AddResident(
	struct Resident	*tagptr,
	struct MemList	*mlptr,
	long 			kickarray[]);

UWORD CercaResident(
	char			*nome,
	UBYTE			vers,
	struct Resident	**tagpp);

void ResetFunz(void);

/* DEFINE */

#define MYVERS	1
#define MYPRI	-1
#define MYNAME	"DemoResident"
#define MYID	"DemoResident 1.00 (1 Febbraio 90)"

#define NUMSEGMENTI 3

#define newkick 0
#define oldkick 1

#define CERCA_NOTFOUND		0x0000
#define CERCA_RESMODULES	0x0001
#define CERCA_KICKTAGPTR	0x0002
#define CERCA_PANICO		0x0004

/* MACRO */

#define BIT31 (1<<31)
#define ISBIT31(x) ( ((long)(x)) & BIT31 )
#define BIT31ON(x) (  (x)!=0 ? ((long)(x)) |  BIT31 : (0) )
#define BIT31OFF(x) ( (x)!=0 ? ((long)(x)) & ~BIT31 : (0) )

/* STRUTTURE */

/* Ridefinisco la struttura MemList di Exec per comodita' */
struct customML
{
struct Node ml_Node;
UWORD ml_NumEntries;
struct MemEntry ml_ME[NUMSEGMENTI];
};

/* ALLOCAZIONE DI VARIABILI */

struct Resident tag;
struct customML miaMemList;
long kickarray[2];

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
BPTR stdout;

char *messaggi[]=
{
"Il modulo e' stato installato\n",
"Il modulo e' stato installato nuovamente (c'era gia' solo in ResModules)\n",
"Errore : il modulo era gia' nella lista KickTagPtr\n",
"Errore : il modulo era gia' sia in ResModules che in KickTagPtr\n",
"PANICO : l'indirizzo del modulo in ResModules e' diverso da quello in KickTagPtr\n",
"Errore : il numero di hunk era maggiore di 3\n"
};

/* Queste due variabili verranno usate da ResetFunz in fase di reset */
struct IntuitonBase *IntuitionBase;

/* Questa variabile per DisplyAlert() genera un WARNING : non se ne tenga conto */
unsigned char stringa[]="\x01\x08\x20ATTENZIONE\x00\x01\x00\x18\x38DemoResident si e' perfettamente installato im memoria !!!\x00\x01\x00\x18\x48Premi il pulsante del mouse per continuare\x00";

/* CODICE */

/* Questa funzione costituisce l'inizio effettivo del programma,
 * quando viene lanciato da CLI */

void start()
{
struct Resident *tagptr;
long *segptr;
UWORD ritorno;

SysBase=*(struct ExecBase **)(4L);

if((DOSBase=(struct DosLibrary *)OpenLibrary(DOSNAME,0L))==NULL) return;

stdout=Output();

/* Controllo che il modulo non sia gia' installato in qualche modo */
ritorno=CercaResident(MYNAME, MYVERS, &tagptr);

/* Carichiamo il modulo se non esiste, oppure se compare solo in ResModules */
if(ritorno==CERCA_NOTFOUND || ritorno==CERCA_RESMODULES)
	{
	/* Creiamo la MemList */
	segptr=PreparaMemList((struct MemList *)&miaMemList, NUMSEGMENTI);

	/* Se c'e' stato qualche problema ritorniamo con il messaggio 5 */
	if(segptr==NULL) ritorno=5;
	else
		{
		/* Preparo la struttura Resident */
		PreparaResident(&tag, MYVERS, (BYTE)MYPRI, MYNAME, MYID, ResetFunz);

		/* Aggiungo il modulo al sistema */
		AddResident(&tag,(struct MemList *)&miaMemList,&kickarray[0]);

		/* Impedisco al CLI di liberare la mia memoria */
		*segptr=0L;
		}
	}

/* Stampiamo il messaggio */
if(stdout) Write(stdout,messaggi[ritorno],strlen(messaggi[ritorno]));

if(DOSBase) CloseLibrary((struct Library *)DOSBase);
}

/**** ResetFunz ****/
/* Questa e' la funzione che verra' eseguita durante il boot */

void ResetFunz()
{
IntuitionBase=OpenLibrary("intuition.library",0L);
if(IntuitionBase==0) return;

DisplayAlert(0L,&stringa[0],200);
}
/* Fine ResetFunz() */

/**** PreparaResident ****************************************
* Inizializza struttura Resident
*
* INPUT:
*   tagptr : puntatore a struttura Resident da inizializzare
*   vers   : numero versione
*   pri    : priorita'
*   nome   : nome
*   id     : stringa di identificazione
*   init   : funzione da chiamare in fase di reset
*
* RITORNO:
*   nessuno
**************************************************************/

void PreparaResident(
	struct Resident	*tagptr,
	UBYTE			vers,
	BYTE			pri,
	char			*nome,
	char			*id,
	void			(*init)())
{
/* Questi due valori sono obbligatori e servono probabilmente al sistema
per verificare se si tratta effettivamente di una struttura Resident; il
primo valore corrisponde a una istruzione illegale nel set del 68000 */

tagptr->rt_MatchWord=RTC_MATCHWORD;

tagptr->rt_MatchTag=tagptr;

/* Il valore che segue rimane piuttosto misterioso:  si sa che deve puntare
a una locazione di memoria che non sia posta all'interno del nostro codice;
puo' puntare alla locazione di memoria che segue immediatamente la
struttura Resident */

tagptr->rt_EndSkip=(APTR)(tagptr+1);

/* Il campo seguente indica di passare il controllo alla routine puntata da
* rt_Init, in fase di cold reset, se contiene RTF_COLDSTART; si puo' usare
* anche anche RTF_AUTOINIT per comandare al sistema di inizializzare certe
* aree di memoria, viene generalmente usato dalle librerie */

tagptr->rt_Flags=RTF_COLDSTART;

/* Introduciamo ora versione nome e stringa di identificazione */

tagptr->rt_Version=vers;
tagptr->rt_Name=nome;
tagptr->rt_IdString=id;

/* Ora priorita' e tipo di tag ( i valori sono gli stessi usati per i nodi,
 * come NT_LIBRARY).  La priorita' indica l'ordine di inizializzazione.
 * Occorre richiedere una priorita' perlomeno superiore a -60 (quella del
 * modulo "strap") per precedere il boot dal disco (nella versione 1.3 occorre
 * porsi a priorita' maggiore di -40 per precedere "romboot") */

tagptr->rt_Type=NT_UNKNOWN;
tagptr->rt_Pri=pri;

/* Il prosssimo campo deve contenere l'indirizzo della routine che verra'
 * chiamata in fase di reset */

tagptr->rt_Init=(APTR)init;
}
/* Fine PreparaResident() */


/**** PreparaMemList ****************************************
* La funzione individua i segmenti del processo chiamante, ne
* memorizza i parametri nella struttura indirizzata da mlptr
* fino al valore massimo espresso da numentries; ritorna un
* puntatore alla locazione cli_Module (da azzerare per evitare
* che il CLI liberi la nostra memoria) se tutto va bene,
* altrimenti NULL.  Ritorna NULL se il processo e' lanciato dal
* WorkBench.
*
* INPUT:
*   mlptr      : puntatore a struttura MemList da inizializzare
*   numentries : numero di entry previste
*
* RITORNO:
*   puntatore a cli_Module del processo chiamante, da azzerare
*   per sganciarsi dal Dos e rimanere in memoria oppure NULL in
*   caso di fallimento
**************************************************************/

long *PreparaMemList(
	struct MemList	*mlptr,
	short			numentries)
{
struct Process *myproc;
struct CommandLineInterface *cliptr;
long *clisegptr;
register long *segmento;
register short i;

/* Trovo l'indirizzo del mio processo */
myproc=(struct Process *)FindTask(0);

/* Trovo il puntatore alla struttura cli */
cliptr=(struct CommandLineInterface *)BADDR(myproc->pr_CLI);

/* Controlla che non sia un processo WorkBench */
if(cliptr==NULL) return NULL;

/* Calcolo l'indirizzo del puntatore al primo segmento, che costituira'
 * il valore di ritorno, se tutto andra' bene */
clisegptr=&cliptr->cli_Module;

/* Il ciclo che segue percorre la lista dei segmenti immagazzinando nelle
 * entry della MemList gli indirizzi della memoria e la loro lunghezza */

for(segmento=clisegptr, i=0;
	segmento=(long *)BADDR(*segmento);
	i++)
	{
	/* Se il numero di hunk e' superiore alla capienza della MemList
	 * indicata da numentries, ritorna NULL */
	if(i>=numentries) return NULL;

	/* Carico la lunghezza del segmento nella i-esima entry della MemList */
	mlptr->ml_ME[i].me_Length=*(segmento-1);

	/* Carico l'indirizzo del segmento nella i-esima entry della MemList */
	mlptr->ml_ME[i].me_Addr=(APTR)(segmento-1);
	}

/* Se i==0 il segmento e' vuoto e ritorna NULL; non dovrebbe accadere */
if(i==0) return NULL;

mlptr->ml_NumEntries=i;

return clisegptr;
}
/* Fine PreparaMemList() */


/**** AddResident *******************************************
* La funzione collega la MemList e il romtag a ExecBase e
* ricalcola KickCheckSum.
*
* INPUT:
*   tagptr : puntatore a struttura Resident da rendere residente
*   mlptr  : puntatore a struttura MemList da rendere residente
*   kickarray : puntatore ad array di due longword da collegare
*               a KickTagPtr
*
* RITORNO:
*   nessuno
**************************************************************/

void AddResident(
	struct Resident	*tagptr,
	struct MemList	*mlptr,
	long			kickarray[])
{
Disable();

/* Collego la mia MemList alla lista unidirezionale di ExecBase */
mlptr->ml_Node.ln_Succ=(struct Node *)SysBase->KickMemPtr;
SysBase->KickMemPtr=(APTR)mlptr;

/* Collego la mia struttura Resident all'array di KickTagPtr */
kickarray[newkick]=(long)tagptr;
kickarray[oldkick]=(long)BIT31ON(SysBase->KickTagPtr);
SysBase->KickTagPtr=(APTR)kickarray;

/* Ricalcolo e salvo checksum */
SysBase->KickCheckSum=(APTR)SumKickData();

Enable();
}
/* Fine AddResident() */


/**** CercaResident **************************************************
* La funzione cerca il romtag con il nome e la versione (se
* diversa da 0) specificata, sia nella lista ResModules sia
* in quella KickTag.
*
* INPUT:
*   nome   : nome del modulo
*   vers   : numero di versione oppure 0 per qualsiasi versione
*   tagpp  : puntatore a puntatore a struttura Resident
*
* RITORNO:
*   CERCA_NOTFOUND (0) se non lo trova
*   CERCA_RESMODULES (1) se lo trova nella lista ResModules
*   CERCA_KICKTAGPTR (2) se lo trova nella lista KickTagPtr
*   CERCA_KICKTAGPTR|CERCA_RESMODULES (3) se lo trova in entrambi
*   CERCA_PANICO (4) se il valore ResModules e quello KickTagPtr
*      sono diversi
*   Il puntatore indirizzato da "tagpp" conterra' l'indirizzo del
*      romtag della lista KickTagPtr se e' stato trovato (anche in caso
*      di PANICO), altrimenti quello di ResModules, altrimenti 0.
***********************************************************************/

UWORD CercaResident(
	char			*nome,
	UBYTE			vers,
	struct Resident	**tagpp)
{
register struct Resident *tagptr;
register long *kickarrayptr;
UWORD ritorno=CERCA_NOTFOUND;

/* E' nella lista ResModules ? */
*tagpp=FindResident(nome);

/* Controlla la versione */
if(*tagpp && ( (vers==0) || ((*tagpp)->rt_Version==vers) )) 
	ritorno=CERCA_RESMODULES;

Disable();

/* Cerca ora in KickTagPtr */

if((kickarrayptr=(long *)SysBase->KickTagPtr)!=NULL)
	while(tagptr=(struct Resident *)*kickarrayptr)
		{
		kickarrayptr++;

		/* Se il bit 31 e' impostato, si tratta di un puntatore a un array */
		if( ISBIT31(tagptr) ) kickarrayptr = (long *) BIT31OFF(tagptr);

		/* Altrimenti controlla versione e nome */
		else if((vers==0 || tagptr->rt_Version==vers) &&
				(strcmp(nome,tagptr->rt_Name)==0))
				{
				/* Se coincidono, allora controlla se l'indirizzo del romtag
				 * e' differente da quello ricavato da ResModules */
				if(*tagpp==0 || *tagpp==tagptr) ritorno |= CERCA_KICKTAGPTR;
				else ritorno=CERCA_PANICO;
				/* Pone nel puntatore l'indirizzo del romtag */
				*tagpp=tagptr;
				break;
				}
		}

Enable();

return ritorno;
}
/* Fine CercaResident() */
