/**************************************************************/
/* $VER: ComTask.c Revison 1.0                                */
/* This file is part of Synth Librarian v1.0                  */
/* ©1993-1994 Jean-Alexis MONTIGNIES                          */
/* This file must not be distributed modified or separatly    */
/* without my written permission                              */
/* See Synth_Librarian.guide for more details                 */
/**************************************************************/

/* Synth librarian part for I/O with serial device */

#include <proto/exec.h>
#include <proto/dos.h>
#include <dos/dos.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <libraries/locale.h>
#include <proto/locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "SynthLib.h"
#define CATCOMP_NUMBERS
#define CATCOMP_BLOCK
#include "ComTaskLoc.h"

struct LocaleInfo CLi;
struct MsgPort *ComPort;
struct IOExtSer *SerialIN,*SerialOUT;
struct timerequest *TimerIO;
ULONG (* DriverPtr)(ULONG *StepPtr,struct Exchange *Ex);
extern struct MsgPort *MainPort;
extern struct library *LocaleBase;

ULONG Status;
struct Exchange Ex;
ULONG Step;
ULONG MessagePosted;
char FormattedText[64];
BOOL INUsed,OUTUsed,DelayUsed,MessageUsed[2];
struct LibMessage MessTab[2];

STRPTR GetString(struct LocaleInfo *li, LONG stringNum,char *CatCompBlock);
void Request(char *DataPtr,ULONG RemainChars);
void ClearSerial(void);
int ParseINResults( BOOL EOFMode );
void Send(char *DataPtr,ULONG Lenght);
void SendMessage(UBYTE Code,APTR DataPtr);
void ReqDelay(ULONG Duration);
void Advance(void);
ULONG InitComTask(void);
void CloseComTask(void);
void CallDriver(void);

ULONG InitComTask(void)
 {ULONG ReturnCode=0;
  UBYTE i;

  ComPort=NULL;
  SerialIN=NULL;
  SerialOUT=NULL;
  TimerIO=NULL;
  DriverPtr=NULL;
  MessagePosted=0;
  INUsed=OUTUsed=DelayUsed=FALSE;

  if(CLi.li_LocaleBase=LocaleBase)
   {CLi.li_Catalog=OpenCatalog(NULL,"SynthLib/ComTask.catalog",OC_BuiltInLanguage,"english",TAG_DONE);
   }
  if (!(ComPort=CreatePort(0,0)))
    ReturnCode=CODE_INIT_NOPORT;

  for (i=0;i<2;i++)
   {MessTab[i].LMess.mn_ReplyPort=ComPort;
    MessTab[i].Code=i;
    MessageUsed[i]=FALSE;
   }

  if (!ReturnCode)
    if (!(SerialIN=(struct IOExtSer *)CreateExtIO(ComPort,sizeof(struct IOExtSer))))
      ReturnCode=CODE_INIT_NOSERIAL;

  if (!ReturnCode)
    if (OpenDevice(SERIALNAME,0L,(struct IORequest *)SerialIN,0))
     {ReturnCode=CODE_INIT_NOSERIAL;
      DeleteExtIO((struct IORequest *)SerialIN);
      SerialIN=NULL;
     }

  if (!ReturnCode)
   {
#ifndef DEBUG_SL
    SerialIN->io_RBufLen=0x2000;
#else
    SerialIN->io_RBufLen=0x20000;
#endif
    SerialIN->io_Baud=31250;
    SerialIN->io_TermArray.TermArray0=0xFFFEF8F7;
    SerialIN->io_TermArray.TermArray1=0xF0F0F0F0;
    SerialIN->io_SerFlags|=SERF_RAD_BOOGIE|SERF_EOFMODE|SERF_XDISABLED;
    SerialIN->IOSer.io_Command=SDCMD_SETPARAMS;
    ReturnCode=DoIO((struct IORequest *)SerialIN);
    if (ReturnCode)
      ReturnCode=CODE_INIT_NOSERIAL;
   }

  if (!ReturnCode)
    if (SerialOUT=(struct IOExtSer *)CreateExtIO(ComPort,sizeof(struct IOExtSer)))
     {memcpy(SerialOUT,SerialIN,sizeof(struct IOExtSer));
      SerialOUT->io_SerFlags&=~SERF_EOFMODE;
     }
     else
      ReturnCode=CODE_INIT_NOSERIAL;

  if (!ReturnCode)
    if (!(TimerIO=(struct timerequest *)CreateExtIO(ComPort,sizeof(struct timerequest))))
      ReturnCode=CODE_INIT_NOTIMER;

  if (!ReturnCode)
    if (OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimerIO,0))
     {ReturnCode=CODE_INIT_NOTIMER;
      DeleteExtIO((struct IORequest *)TimerIO);
      TimerIO=NULL;
     }

  if (!ReturnCode)
      SendMessage(MSGCODE_INFO,GetString(&CLi,ReturnCode,ComCatBlock));

  if (ReturnCode)
    if (ReturnCode==CODE_INIT_NOSERIAL && SerialIN->IOSer.io_Error && SerialIN)
      SendMessage(MSGCODE_ERROR,GetString(&CLi,MSG_SERIAL-1+SerialIN->IOSer.io_Error,ComCatBlock));
     else
      SendMessage(MSGCODE_ERROR,GetString(&CLi,MSG_INIT+ReturnCode,ComCatBlock));

  return(ReturnCode);
 }

void CloseComTask()
 {struct Message *Mess;

  if (DriverPtr)
   {Step=STEP_EXIT_DRIVER;
    DriverPtr(&Step,&Ex);
   }

  if (TimerIO && DelayUsed)
    AbortIO((struct IORequest *)TimerIO);

  if (SerialOUT && OUTUsed)
    AbortIO((struct IORequest *)SerialOUT);

  if (SerialIN && INUsed)
    AbortIO((struct IORequest *)SerialIN);

  if (ComPort)
   {do
     {while(Mess=GetMsg(ComPort))
        if (Mess->mn_ReplyPort!=ComPort)
         {if (Mess->mn_ReplyPort==MainPort)
            ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
          ReplyMsg(Mess);
         }
         else
         {MessagePosted--;
          if (((struct LibMessage *)Mess)->Code>=2)
            FreeMem((APTR)Mess,sizeof(struct LibMessage));
         }
      if (MessagePosted)
        Wait(1<<(ComPort->mp_SigBit));
     }
    while(MessagePosted);
   }

  if (TimerIO)
   {CloseDevice((struct IORequest *)TimerIO);
    DeleteExtIO((struct IORequest *)TimerIO);
   }

  if (SerialIN)
   {CloseDevice((struct IORequest *)SerialIN);
    DeleteExtIO((struct IORequest *)SerialIN);
   }

  if (SerialOUT)
    DeleteExtIO((struct IORequest *)SerialOUT);

  MessTab[0].LMess.mn_ReplyPort=NULL;
  MessTab[0].Code=MSGCODE_BYE;
  MessTab[0].Data=GetString(&CLi,MSG_BYE,ComCatBlock);
  PutMsg(MainPort,(struct Message *)&MessTab[0]);

  if (LocaleBase)
   {CloseCatalog(CLi.li_Catalog);
   }

  Wait(SIGBREAKF_CTRL_C);

  if (ComPort)
   {while(Mess=GetMsg(ComPort))
     {if (Mess->mn_ReplyPort==MainPort)
        ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
      ReplyMsg(Mess);
     }

    DeletePort(ComPort);
   }
 }


void Request(char *DataPtr,ULONG RemainChars)
 {
  SerialIN->IOSer.io_Command=CMD_READ;
  SerialIN->IOSer.io_Length=min(RemainChars,128);
  SerialIN->IOSer.io_Data=(APTR)DataPtr;
  SerialIN->IOSer.io_Flags|=IOF_QUICK;
  BeginIO((struct IORequest *)SerialIN);

  if (!(SerialIN->IOSer.io_Flags & IOF_QUICK))
   {MessagePosted++;
    INUsed=TRUE;
   }
 }

void Send(char *DataPtr,ULONG Length)
 {SerialOUT->IOSer.io_Command=CMD_WRITE;
  SerialOUT->IOSer.io_Length=Length;
  SerialOUT->IOSer.io_Data=(APTR)DataPtr;
  SerialOUT->IOSer.io_Flags|=IOF_QUICK;
  BeginIO((struct IORequest *)SerialOUT);

  if (!(SerialOUT->IOSer.io_Flags & IOF_QUICK))
   {MessagePosted++;
    OUTUsed=TRUE;
   }
 }

void ClearSerial()
 {SerialIN->IOSer.io_Command=CMD_CLEAR;
  SerialIN->IOSer.io_Flags|=IOF_QUICK;
  BeginIO((struct IORequest *)SerialIN);

  if (!(SerialIN->IOSer.io_Flags & IOF_QUICK))
   {MessagePosted++;
    INUsed=TRUE;
   }
 }

void SendMessage(UBYTE Code,APTR DataPtr)
 {struct LibMessage *Mess;

  if (Code>=2)
   { /* Message très important */
    Mess=(struct LibMessage *)AllocMem(sizeof(struct LibMessage),0L);
    if (Mess)
     {Mess->LMess.mn_Length=sizeof(struct LibMessage);
      Mess->LMess.mn_ReplyPort=ComPort;
      Mess->Code=Code;
      Mess->Data=DataPtr;

      PutMsg(MainPort,(struct Message *)Mess);
      MessagePosted++;
     }
     else
     /* Cas désespéré, on efface les données recues */
     {
      if (Code==MSGCODE_DATA || (Code==MSGCODE_REQ_RESULT && DataPtr))
       {
        FreeMem(((struct DumpNode *)DataPtr)->Dump,((struct DumpNode *)DataPtr)->DumpLength);
        if (((struct DumpNode *)DataPtr)->Data)
          FreeMem(((struct DumpNode *)DataPtr)->Data,((struct DumpNode *)DataPtr)->DataLength);
        FreeMem(DataPtr,sizeof(struct DumpNode));
       }
     }
   }
   else
     /* Message moins important */
    if (!MessageUsed[Code])
     {MessageUsed[Code]=TRUE;
      MessTab[Code].Data=DataPtr;

      PutMsg(MainPort,(struct Message *)(&MessTab[Code]));
      MessagePosted++;
     }
 }

void ReqDelay(ULONG Duration)
 { if (DelayUsed)
    {AbortIO((struct IORequest *)TimerIO);
     WaitIO((struct IORequest *)TimerIO);
     MessagePosted--;
    }

   TimerIO->tr_node.io_Command=TR_ADDREQUEST;
   TimerIO->tr_time.tv_secs=Duration/1000000;
   TimerIO->tr_time.tv_micro=Duration%1000000;
   SendIO((struct IORequest *)TimerIO);

   DelayUsed=TRUE;
   MessagePosted++;

 }

void CallDriver()
 {int Error=0;

  Ex.Message=NULL;

  Error=DriverPtr(&Step,&Ex);
  if (Error)
    SendMessage(MSGCODE_ERROR,Ex.Message);

  if (Ex.Message)
    SendMessage(MSGCODE_INFO,Ex.Message);
 }

int ParseINResults(BOOL EOFMode)
 {int ReturnCode=0;
  BOOL EofReached;

  if (SerialIN->IOSer.io_Error)
    ReturnCode=3;
   else
   {Ex.Length-=SerialIN->IOSer.io_Actual;
    Ex.DataPtr+=SerialIN->IOSer.io_Actual;

    EofReached=(Ex.DataPtr[-1]==(Ex.Data1 & 0xFF))||(Ex.DataPtr[-1]==((Ex.Data1>>8)& 0xFF))\
             ||(Ex.DataPtr[-1]==((Ex.Data1>>16) & 0xFF))||(Ex.DataPtr[-1]==(Ex.Data1>>24));
    if (!(EofReached && EOFMode))
      if (Ex.DataPtr[-1]==0xF8 || Ex.DataPtr[-1]==0xFE)
       {Ex.Length++;
        Ex.DataPtr--;
        ReturnCode=1;
       }
       else
         if (Ex.DataPtr[-1]>0x7F)
           ReturnCode=2;
          else
           if (Ex.Length)
             ReturnCode=1;
   }
  return(ReturnCode);
 }

void Advance(void)
 {static unsigned char TempBuffer[0x80];
  int Error=0;

  switch(Step)
   {

/************************************************************/
/*                        Réception                         */
/************************************************************/

    case STEP_RECIEVE_COMPLETE:
      SendMessage(MSGCODE_DATA,Ex.Node);
      Step=STEP_WAIT;
      Status&=~STATUS_RECIEVING;

      break;

    case STEP_ABORT:     /* Annulation */
      if (INUsed)
        AbortIO((struct IORequest *)SerialIN);

      CallDriver();
      Step=STEP_WAIT;

      Status&=~STATUS_RECIEVING;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_IO_ERROR:     /* En cas d'erreur */
      SendMessage(MSGCODE_ERROR,GetString(&CLi,MSG_SERIAL-1+SerialIN->IOSer.io_Error,ComCatBlock));
      ClearSerial();

      if (Status & STATUS_RECIEVING)
       {Step=STEP_ABORT;
        CallDriver();
       }

      Step=STEP_WAIT;
      Status=(Status&~STATUS_RECIEVING)|STATUS_WAIT_IN;

      break;

    case STEP_WAIT:      /* Attente */
    
    Status&=~STATUS_RECIEVING;
    
    case STEP_NEXT_SYSEX:
      if (INUsed)
        Status|=STATUS_WAIT_IN;
       else
       {
        Step++;

        Request(TempBuffer,0x80);
        Status|=STATUS_WAIT_IN;
       }

      break;

    case STEP_NEXT_SYSEX+1:
    case STEP_START:     /* Attente d'un début de message exclusif */
      if (SerialIN->IOSer.io_Error)
        Step=STEP_IO_ERROR;
       else
        if ((TempBuffer[SerialIN->IOSer.io_Actual-1]==0xF0) && DriverPtr)
         {Ex.Length=0;
          if (Step!=STEP_START)
            Step=STEP_CONTINUE;
           else
            Status|=STATUS_RECIEVING;
          CallDriver();
         }
         else
          Step--;
          
       break;

    case STEP_RECIEVE:
      Request(Ex.DataPtr,Ex.Length);
      Step=STEP_CONTINUE;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_CONTINUE:
      switch(ParseINResults(0))
       {case 0:
          CallDriver();

          break;

        case 1:
          Step=STEP_RECIEVE;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_ABORT;

          break;

        case 3:
          Step=STEP_IO_ERROR;

          break;
       }

      break;

    case STEP_RECIEVE_EOF:
      Request(Ex.DataPtr,Ex.Length);
      Step=5;
      Status|=STATUS_WAIT_IN;

      break;

    case 5:
      switch(ParseINResults(1))
       {case 0:
          Step=STEP_CONTINUE;
          CallDriver();

          break;

        case 1:
          Step=STEP_RECIEVE_EOF;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_ABORT;

          break;

        case 3:
          Step=STEP_IO_ERROR;

          break;
       }

      break;

/************************************************************/
/*                        Emission                          */
/************************************************************/

    case STEP_SEND_COMPLETE:
      SendMessage(MSGCODE_SEND_FINISHED,Ex.Node);

      Status&=~STATUS_SENDING;
      Step=STEP_WAIT;

      break;

    case STEP_SEND_ABORT:
      CallDriver();

      SendMessage(MSGCODE_SEND_FINISHED,Ex.Node);

      if (OUTUsed)
        AbortIO((struct IORequest *)SerialOUT);

      if (DelayUsed)
        AbortIO((struct IORequest *)TimerIO);

      if (INUsed)
        AbortIO((struct IORequest *)SerialIN);

      Status&=~(STATUS_SENDING+STATUS_MASK_WAIT);
      Status|=STATUS_WAIT_ALL;
      Step=STEP_WAIT;

      break;

    case STEP_SEND_START:
    case STEP_SEND_CONTINUE:
      CallDriver();

      break;

    case STEP_SEND_SEND:
      Step=STEP_SEND+4;

      Status|=STATUS_WAIT_OUT;
      break;

    case STEP_SEND+4:
      if (SerialOUT->IOSer.io_Error)
        Step=STEP_SEND_ABORT;
       else
       {Send(Ex.DataPtr,Ex.Length);
        Step=STEP_SEND_CONTINUE;
        CallDriver();
       }

      break;

    case STEP_SEND_DELAY:
      ReqDelay(Ex.Data1);
      Status|=STATUS_WAIT_DELAY;

      Step=STEP_SEND_CONTINUE;

      break;

    case STEP_SEND_IO_ERROR:     /* En cas d'erreur */
      SendMessage(MSGCODE_ERROR,GetString(&CLi,MSG_SERIAL-1+SerialIN->IOSer.io_Error,ComCatBlock));
      ClearSerial();

      Step=STEP_SEND_ABORT;
      
      Status=STATUS_WAIT_IN;

      break;

    case STEP_SEND_WAIT:      /* Attente */
      if (INUsed)
        Status|=STATUS_WAIT_IN;
       else
       {
        Step=STEP_SEND+7;

        Request(TempBuffer,0x80);
        Status|=STATUS_WAIT_IN;
       }

      break;

    case STEP_SEND+7:     /* Attente d'un début de message exclusif */
      if (SerialIN->IOSer.io_Error)
        Step=STEP_SEND_IO_ERROR;
       else
        if ((TempBuffer[SerialIN->IOSer.io_Actual-1]==0xF0) && DriverPtr)
         {Ex.Length=0;
          Step=STEP_SEND_CONTINUE;
          CallDriver();
         }
         else
          Step=STEP_SEND_WAIT;

      break;

    case STEP_SEND_RECIEVE:
      Request(Ex.DataPtr,Ex.Length);
      Step=STEP_SEND+9;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_SEND+9:
      switch(ParseINResults(0))
       {case 0:
          Step=STEP_SEND_CONTINUE;
          CallDriver();

          break;

        case 1:
          Step=STEP_SEND_RECIEVE;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_SEND_ABORT;

          break;

        case 3:
          Step=STEP_SEND_IO_ERROR;

          break;
       }

      break;

    case STEP_SEND_RECIEVE_EOF:
      Request(Ex.DataPtr,Ex.Length);
      Step=STEP_SEND+11;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_SEND+11:
      switch(ParseINResults(1))
       {case 0:
          Step=STEP_SEND_CONTINUE;
          CallDriver();

          break;

        case 1:
          Step=STEP_SEND_RECIEVE_EOF;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_SEND_ABORT;

          break;

        case 3:
          Step=STEP_SEND_IO_ERROR;

          break;
       }

      break;

/************************************************************/
/*                        Requette                          */
/************************************************************/

    case STEP_REQ_ABORT:     /* Annulation */
      if (INUsed)
        AbortIO((struct IORequest *)SerialIN);

      if (OUTUsed)
        AbortIO((struct IORequest *)SerialOUT);

      CallDriver();
      Step=STEP_WAIT;

      Ex.Node=NULL;
      SendMessage(MSGCODE_REQ_RESULT,Ex.Node);

      Status&=~STATUS_REQUEST;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_REQ_IO_ERROR:     /* En cas d'erreur */

      SendMessage(MSGCODE_ERROR,GetString(&CLi,MSG_SERIAL+SerialIN->IOSer.io_Error-1,ComCatBlock));
      ClearSerial();

      Step=STEP_REQ_ABORT;
      CallDriver();

      Ex.Node=0;
      SendMessage(MSGCODE_REQ_RESULT,Ex.Node);

      Step=STEP_WAIT;
      Status=(Status&~STATUS_REQUEST)|STATUS_WAIT_IN;

      break;

    case STEP_REQ_WAIT:      /* Attente */
      if (INUsed)
        Status|=STATUS_WAIT_IN;
       else
       {
        Step=STEP_REQ+5;

        Request(TempBuffer,0x80);
        Status|=STATUS_WAIT_IN;
       }

      break;

    case STEP_REQ+5:     /* Attente d'un début de message exclusif */
      if (SerialIN->IOSer.io_Error)
        Step=STEP_REQ_IO_ERROR;
       else
        if (TempBuffer[SerialIN->IOSer.io_Actual-1]==0xF0)
         {Ex.Length=0;
          Step=STEP_REQ_CONTINUE;
          CallDriver();
         }
         else
          Step=STEP_REQ_WAIT;

      break;

    case STEP_REQ_RECIEVE:
      Request(Ex.DataPtr,Ex.Length);
      Step=STEP_REQ_CONTINUE;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_REQ_CONTINUE:
          switch(ParseINResults(0))
       {case 0:
          CallDriver();

          break;

        case 1:
          Step=STEP_REQ_RECIEVE;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_REQ_ABORT;

          break;

        case 3:
          Step=STEP_REQ_IO_ERROR;

          break;
       }

       break;

    case STEP_REQ_RECIEVE_EOF:
      Request(Ex.DataPtr,Ex.Length);
      Step=STEP_REQ+9;
      Status|=STATUS_WAIT_IN;

      break;

    case STEP_REQ+9:
      switch(ParseINResults(1))
       {case 0:
          Step=STEP_REQ_CONTINUE;
          CallDriver();

          break;

        case 1:
          Step=STEP_REQ_RECIEVE_EOF;

          break;

        case 2:
          SendMessage(1,GetString(&CLi,MSG_UNEXPECTED,ComCatBlock));
          Step=STEP_REQ_ABORT;

          break;

        case 3:
          Step=STEP_REQ_IO_ERROR;

          break;
       }

      break;

    case STEP_REQ_COMPLETE:
      SendMessage(MSGCODE_REQ_RESULT,Ex.Node);

      Status&=~STATUS_REQUEST;
      Step=STEP_WAIT;

      break;

    case STEP_REQ_START:
      if (INUsed)
        AbortIO((struct IORequest *)SerialIN);
      CallDriver();

      break;

    case STEP_REQ_SEND:
      Step=STEP_REQ+2;

      Status|=STATUS_WAIT_OUT_IN;
      break;

    case STEP_REQ+2:
      if (SerialOUT->IOSer.io_Error)
        Step=STEP_REQ_ABORT;
       else
       {Send(Ex.DataPtr,Ex.Length);
        Step=STEP_REQ_CONTINUE;
        CallDriver();
       }

      break;
   }
 }

void __saveds ComTask()
 {
  BOOL HTWait;
  ULONG MessMask,Signals;
  struct Message *Mess;
  int Error;
  UBYTE Code;
  UBYTE *DataPtr;
  Status=0;
  Error=0;

  Error=InitComTask();
  Step=STEP_WAIT;

  if (!Error)
   {
    MessMask= 1<<(ComPort->mp_SigBit);

    while (!((Status & STATUS_EXIT)&& !(Status & STATUS_NOEXIT)))
     {
      if ((Status & STATUS_QUIT_DRIVER) && !(Status & STATUS_NOEXIT) && DriverPtr)
       {ULONG Step2;

        Step2=Step;
        Step=STEP_EXIT_DRIVER;
        DriverPtr(&Step,&Ex);
        DriverPtr=NULL;
        Status&=~STATUS_QUIT_DRIVER;
        SendMessage(MSGCODE_DRIVER_QUITED,0);
        Step=Step2;
       }

      switch(Status & STATUS_MASK_WAIT)
       {case STATUS_WAIT_IN:

          HTWait=INUsed;
          break;

        case STATUS_WAIT_OUT:

          HTWait=OUTUsed;
          break;

        case STATUS_WAIT_DELAY:

          HTWait=DelayUsed;
          break;

        case STATUS_WAIT_OUT_DELAY:

          HTWait=DelayUsed || OUTUsed;
          break;

        case STATUS_WAIT_OUT_IN:

          HTWait=INUsed || OUTUsed;
          break;

        case STATUS_WAIT_ALL:

          HTWait=INUsed || OUTUsed || DelayUsed;
          break;

        default:

          HTWait=FALSE;
       }

      if (HTWait)
       {/* Attente de signaux */
        Signals=Wait(MessMask);
        /* S'il s'agit d'un message */
        if (Signals & MessMask)
          /* Récuperation du message */
          while(Mess=GetMsg(ComPort))
           {/* Est-ce la réponse a un de nos message ?*/
            if (Mess->mn_ReplyPort==ComPort)
             {MessagePosted--;
              if (Mess==&(SerialIN->IOSer.io_Message))
                INUsed=FALSE;
               else
                if (Mess==&(SerialOUT->IOSer.io_Message))
                 {OUTUsed=FALSE;
                  if (SerialOUT->IOSer.io_Error==-2)
                    SerialOUT->IOSer.io_Error=0;
                 }
                 else
                  if (Mess==&(TimerIO->tr_node.io_Message))
                    DelayUsed=FALSE;
                   else
                    if (((struct LibMessage *)Mess)->Code>=2)
                      FreeMem(Mess,sizeof(struct LibMessage));
                     else
                      MessageUsed[((struct LibMessage *)Mess)->Code]=FALSE;
             }
             else
             {Code=((struct LibMessage *)Mess)->Code;
              DataPtr=((struct LibMessage *)Mess)->Data;

              if (DriverPtr || (Code==MSGCODE_NEW_DRIVER) || (Code==MSGCODE_EXIT))
                switch(Code)
                 {
                  case MSGCODE_ABORT:
                    if (Status & STATUS_REQUEST)
                     {Status&=~STATUS_MASK_WAIT;
                      Step=STEP_REQ_ABORT;
                     }
                    if (Status & STATUS_RECIEVING)
                     {Status&=~STATUS_MASK_WAIT;
                      Step=STEP_ABORT;
                     }
                    if (Status & STATUS_SENDING)
                     {Status&=~STATUS_MASK_WAIT;
                      Step=STEP_SEND_ABORT;
                     }
                    break;

                  case MSGCODE_SEND:
                    if (!(Status & STATUS_NOEXIT))
                     {Ex.Node=(struct DumpNode *)DataPtr;
                      Step=STEP_SEND_START;
                      Status&=~STATUS_MASK_WAIT;
                      Status|=STATUS_SENDING;
                     }
                     else
                     ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
                    break;

                  case MSGCODE_EXIT:
                    Status|=STATUS_EXIT;
                    break;

                  case MSGCODE_REQUEST:
                    if (!(Status & STATUS_NOEXIT))
                     {Ex.Node=(struct DumpNode *)DataPtr;
                      Step=STEP_REQ_START;
                      Status&=~STATUS_MASK_WAIT;
                      Status|=STATUS_REQUEST;
                     }
                     else
                      ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
                    break;

                  case MSGCODE_QUIT_DRIVER:
                    Status|=STATUS_QUIT_DRIVER;
                    break;

                  case MSGCODE_NEW_DRIVER:
                    DriverPtr=DataPtr;
                    Step=STEP_INIT_DRIVER;
                    CallDriver();
                    if (Step==STEP_EXIT_DRIVER)
                     {DriverPtr=NULL;
                      ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
                     }
                     else
                     {Step=STEP_WAIT;
                      Status&=~STATUS_MASK_WAIT;
                     }
                    break;
                 }
               else
                 ((struct LibMessage *)Mess)->Code|=MSGCODE_IGNORED;
              ReplyMsg((struct Message *)Mess);
             }
           }
       }
       else
       {Status&=~STATUS_MASK_WAIT;
        Advance();
       }
     }
   }
  CloseComTask();
}
