/* ___________________________________________________________________________
** |*************************************************************************|
** |*|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|*|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|*|
** |*| Set TAB to 4 for best readable file layout.  |*|  C++ MaxonDev 4.0  |*|
** |*|______________________________________________|*|____________________|*|
** |*************************************************************************|
** |*|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|*|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|*|
** |*|               |*|  $Id: rhosigma.c (28.04.01)                       |*|
** |*| ###### ###### |*|  RhoSigma Source based on NDK 3.1 Includes 40.15  |*|
** |*| ##  ## ##   # |*| _________________________________________________ |*|
** |*| ##  ##  ##    |*|                                                   |*|
** |*| ######   ##   |*|  Source file with RhoSigma's support functions    |*|
** |*| ##      ##    |*|                                                   |*|
** |*| ##     ##   # |*| ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ |*|
** |*| ##     ###### |*|  Copyright © 1998-2001 RhoSigma, Roland Heyder    |*|
** |*|               |*|  All Rights Reserved.                             |*|
** |*|_______________|*|___________________________________________________|*|
** |*************************************************************************|
** ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/

/* Struktur-Includes */
#include <exec/types.h>

/* Library-Includes */
#include <stdarg.h>
#include <string.h>
#include <clib/alib_protos.h>
#include <pragma/exec_lib.h>

/* Source-Header */
#include <rhosigma/rhosigma.h>

/*
** ---------------------------------------
** Funktionen aus RhoSigma-Include-Dateien
** ---------------------------------------
*/

/* deklariert in »settings.h« */
VOID DAJOIN(UWORD Count, ...)
{
	STRPTR  TxtPtr;
	WORD	*xPosPtr;
	UWORD   TxtLen, SpcLen, ArgCnt;
	va_list ArgPtr;

	va_start(ArgPtr, Count);
	for (ArgCnt=NULL; ArgCnt<Count; ArgCnt++)
	{
		/* Füllbytes von NULL auf SPACE ändern */
		TxtPtr = va_arg(ArgPtr, STRPTR);
		TxtLen = strlen(TxtPtr+3);
		SpcLen = (3-((TxtLen)&3));
		strncat(TxtPtr+3,"   ",SpcLen);

		/* gewünschte Ausrichtung berechnen */
		xPosPtr = (WORD*) TxtPtr;
			 if (xPosPtr[0] == (WORD) DATPOS_C) xPosPtr[0] = (640-(TxtLen*8))/2
		else if (xPosPtr[0] == (WORD) DATPOS_L) xPosPtr[0] = 12;
		else if (xPosPtr[0] == (WORD) DATPOS_R) xPosPtr[0] = (640-(TxtLen*8))-12;
	}
}

/* deklariert in »extradefs.h« */
VOID SMTJOIN(UWORD Count, ...)
{
	STRPTR  TxtPtr;
	WORD	*xPosPtr;
	UWORD   TxtLen, SpcLen, ArgCnt;
	va_list ArgPtr;

	va_start(ArgPtr, Count);
	for (ArgCnt=NULL; ArgCnt<Count; ArgCnt++)
	{
		/* Füllbytes von NULL auf SPACE ändern */
		TxtPtr = va_arg(ArgPtr, STRPTR);
		if (TxtPtr[3] == (UBYTE) FALSE)
		{
			TxtLen = strlen(TxtPtr+4);
			SpcLen = ((6-((TxtLen)&3))&3);
			strncat(TxtPtr+4,"   ",SpcLen);
		}
		else
		{
			TxtLen = strlen(TxtPtr+9);
			SpcLen = ((5-((TxtLen)&3))&3);
			strncat(TxtPtr+9,"   ",SpcLen);
		}

		/* gewünschte Ausrichtung berechnen */
		xPosPtr = (WORD*) TxtPtr;
			 if (xPosPtr[0] == (WORD) SMTPOS_C) xPosPtr[0] = (640-(TxtLen*8))/2
		else if (xPosPtr[0] == (WORD) SMTPOS_L) xPosPtr[0] = 12;
		else if (xPosPtr[0] == (WORD) SMTPOS_R) xPosPtr[0] = (640-(TxtLen*8))-12;
	}
}

/*
** ----------------------------------------
** Support-Funktionen für Device-Handhabung
** ----------------------------------------
*/

/*** rhosigma / GetIOReq()						V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion belegt und initialisiert einen neuen IORequest, welcher dann
* zur Kommunikation mit Devices verwendet werden kann.
*  Da diese Routine universell einsetzbar sein soll, wird jedoch nur ein APTR
* auf eine beliebige Struktur zurückgegeben, die man dann »casten« kann.
*-----------------------------------------------------------------------------
* Synopsis:		IOReq = GetIOReq (Size)
*
* Eingaben:		Size	--> (UWORD) Größe des benötigten IORequests in Byte.
*
* Ergebnis:		IOReq	--> (APTR) Zeiger auf den neuen IORequest oder NULL,
*								   wenn der Request nicht belegt werden konnte.
*
* Bemerkung:	Die Ursache für das Fehlschlagen dieser Funktion ist entweder
*				Speichermangel, oder es war kein Signal-Bit für den Reply-Port
*				des IORequests mehr frei.
*
* Siehe auch:	»CopyIOReq()«, »FreeIOReq()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

APTR GetIOReq(UWORD Size)
{
	APTR IOReq = NULL;

	/* versuche Reply-Port zu belegen */
	struct MsgPort *Port = CreatePort(NULL,NULL);
	if (Port)
	{
		/* versuche IORequest zu belegen */
		IOReq = (APTR) CreateExtIO(Port,Size);
		if (!IOReq)	   DeletePort(Port);
	}

	/* initialisierter IORequest */
	return IOReq;
}

/*** rhosigma / CopyIOReq()						V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion legt eine Kopie von einem IORequest an, damit man z.B. für
* Lese- und Schreibzugriffe einen seperaten Request zur Verfügung hat.
*  Da diese Routine universell einsetzbar sein soll, wird jedoch nur ein APTR
* auf eine beliebige Struktur zurückgegeben, die man dann »casten« kann.
*-----------------------------------------------------------------------------
* Synopsis:		CopyReq = CopyIOReq(OrigReq)
*
* Eingaben:		OrigReq	--> (APTR) Zeiger auf einen über »exec/OpenDevice()« be-
*								   reits vollständig initialisierten IORequest.
*
* Ergebnis:		CopyReq	--> (APTR) Zeiger auf den neuen IORequest oder NULL,
*								   wenn der Request nicht belegt werden konnte.
*
* Bemerkung:	Die Ursache für das Fehlschlagen dieser Funktion ist entweder
*				Speichermangel, oder es war kein Signal-Bit für den Reply-Port
*				des IORequests mehr frei.
*				 Der mit dieser Funktion erstellte IORequest muß, wenn er nicht
*				mehr benötigt wird, ebenfalls mit »FreeIOReq()« wieder freige-
*				geben werden.
*
*   ACHTUNG:	Sie dürfen niemals einen kopierten IORequest bei einen Aufruf
*   ¯¯¯¯¯¯¯¯	der Funktion »exec/CloseDevice()« verwenden. Benutzen Sie zu
*				diesem Zwecke immer den originalen IORequest, mit dem Sie auch
*				die Funktion »exec/OpenDevice()« aufgerufen haben.
*
* Siehe auch:	»GetIOReq()«, »FreeIOReq()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

APTR CopyIOReq(APTR OrigReq)
{
	/* APTR casten */
	struct IORequest *OldReq = (struct IORequest*) OrigReq;

	/* neuen IORequest mit entsprechender Size anfordern */
	APTR CopyReq = GetIOReq(OldReq -> io_Message.mn_Length);
	if (CopyReq)
	{
		/* neuen APTR jetzt casten */
		struct IORequest *NewReq = (struct IORequest*) CopyReq;

		/* alle wichtigen Einträge übertragen */
		NewReq -> io_Message.mn_Node.ln_Type = OldReq -> io_Message.mn_Node.ln_Type;
		NewReq -> io_Message.mn_Node.ln_Pri	 = OldReq -> io_Message.mn_Node.ln_Pri;
		NewReq -> io_Message.mn_Node.ln_Name = OldReq -> io_Message.mn_Node.ln_Name;
		NewReq -> io_Message.mn_Length		 = OldReq -> io_Message.mn_Length;
		NewReq -> io_Device					 = OldReq -> io_Device;
		NewReq -> io_Unit					 = OldReq -> io_Unit;
	}

	// initialisierter neuer IORequest */
	return CopyReq;
}

/*** rhosigma / FreeIOReq()						V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion gibt einen IORequest und alle damit verbundenen Resourcen
* wieder an das System zurück.
*-----------------------------------------------------------------------------
* Synopsis:		VOID FreeIOReq (IOReq)
*
* Eingaben:		IOReq	--> (APTR) Zeiger auf den freizugebenden IORequest.
*
*   ACHTUNG:	Diese Funktion darf nur für IORequests aufgerufen werden, die
*   ¯¯¯¯¯¯¯¯	entweder direkt mit der Funktion »GetIOReq()«, oder indirekt
*				über die Funktion »CopyIOReq()« erstellt worden sind.
*
* Siehe auch:	»GetIOReq()«, »CopyIOReq()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

VOID FreeIOReq(APTR IOReq)
{
	/* APTR casten */
	struct IORequest *Req = (struct IORequest*) IOReq;

	/* Wenn IORequest != NULL, dann erst Reply-Port und dann */
	/* IORequest selbst freigeben 							 */
	if (Req)
	{
		struct MsgPort *Port = Req -> io_Message.mn_ReplyPort;
		if (Port)			   DeletePort(Port);
		DeleteExtIO(Req);
	}
}

/*** rhosigma / DoCMD()							V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion initialisiert den IORequest mit dem angegbenen Kommando
* und sendet dieses dann im synchronen IO-Modus an das Device.
*-----------------------------------------------------------------------------
* Synopsis:		Error = DoCMD (IOReq, Comm)
*
* Eingaben:		IOReq	--> (APTR)	Zeiger auf den IORequest des anzusprechen-
*									den Devices.
*				Comm	--> (UWORD) Auszuführendes Device-Kommando.
*
* Ergebnis:		Error	--> (BYTE) Ist NULL, wenn alles in Ordnung, sonst eine
*								   Fehlernummer des angesprochenen Devices
*								   (s.a. Device-Includes).
*
* Siehe auch:	»SendCMD()«, »WaitCMD()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

BYTE DoCMD(APTR IOReq,UWORD Comm)
{
	/* APTR casten */
	struct IORequest *Req = (struct IORequest*) IOReq;

	/* IORequest mit Device-Kommando laden und Request ans Device senden */
	/* und Ergebnis des Aufrufs zurückgeben								 */
	Req -> io_Command = Comm;
	return DoIO(Req);
}

/*** rhosigma / SendCMD()						V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion initialisiert den IORequest mit dem angegbenen Kommando
* und sendet dieses dann im asynchronen IO-Modus an das Device.
*-----------------------------------------------------------------------------
* Synopsis:		VOID SendCMD (IOReq, Comm)
*
* Eingaben:		IOReq	--> (APTR)	Zeiger auf den IORequest des anzusprechen-
*									den Devices.
*				Comm	--> (UWORD) Auszuführendes Device-Kommando.
*
* Bemerkung:	Da diese Funktion nicht auf die Beendigung des IO-Vorgangs
*				wartet, kann sie natürlich auch keine Auskunft über Erfolg oder
*				Fehlschlag des selbigen geben. Sie sollten daher, wenn Sie mit
*				den anderen Dingen, welche Sie asynchron nebenherlaufen lassen
*				haben, fertig sind, die Funktion »WaitCMD()« aufrufen, welche
*				sowohl die eventuelle Fehlernummer liefert, sowie die erforder-
*				lichen Abschlußarbeiten erledigt.
*
* Siehe auch:	»DoCMD()«, »WaitCMD()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

VOID SendCMD(APTR IOReq,UWORD Comm)
{
	/* APTR casten */
	struct IORequest *Req = (struct IORequest*) IOReq;

	/* IORequest mit Device-Kommando laden und ans Device senden */
	Req -> io_Command = Comm;
	SendIO(Req);
}

/*** rhosigma / WaitCMD()						V2.118 ***
*»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
* Diese Funktion wartet auf die Beendigung eines zuvor mittels »SendCMD()« ge-
* starteten asynchronen IO-Vorgangs.
*-----------------------------------------------------------------------------
* Synopsis:		Error = WaitCMD (IOReq)
*
* Eingaben:		IOReq	--> (APTR) Zeiger auf einen bereits an ein Device ge-
*								   sendeten IORequest.
*
* Ergebnis:		Error	--> (BYTE) Ist NULL, wenn alles in Ordnung, sonst eine
*								   Fehlernummer des angesprochenen Devices
*								   (s.a. Device-Includes).
*
* Bemerkung:	Mit einem »SendCMD()/WaitCMD()«-Paar erreichen Sie praktisch
*				das geleiche, wie mit einem Aufruf von »DoCMD()«, nur mit dem
*				Unterschied, daß Ihr Task bei letzteren in Wartestatus ver-
*				setzt wird, und Sie bei ersterer Methode während der anfallen-
*				den Wartezeit (z.B. wenn eine Disk geladen wird) anderen Auf-
*				gaben nachgehen können (z.B. Berechnungen etc.). Sollte der
*				IO-Vorgang bereits beendet sein, wenn Sie diese Funktion auf-
*				rufen, dann kehrt sie unverzüglich mit entsprechenden Ergebnis
*				zurück.
*
* Siehe auch:	»DoCMD()«, »SendCMD()«
*«««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««*/

BYTE WaitCMD(APTR IOReq)
{
	/* APTR casten */
	struct IORequest *Req = (struct IORequest*) IOReq;

	/* Auf Beendigung des IO-Vorgangs warten und Fehler merken */
	BYTE Err = WaitIO(Req);

	/* Eventuell noch gesetztes Signal-Bit löschen */
	/* dazu zuerst Signal-Bit ermitteln			   */
	struct MsgPort *Port = Req -> io_Message.mn_ReplyPort;
	ULONG  SigMask		 = 1<<(Port -> mp_SigBit);

	/* Dann Signal-Task ermitteln und Bit löschen */
	struct Task *Tcb	= (struct Task*) Port -> mp_SigTask;
	Tcb -> tc_SigRecvd	= ((Tcb -> tc_SigRecvd)|SigMask)^SigMask;

	/* Funktion beenden und Fehler zurückgeben */
	return Err;
}

