/* Terminal0.c

   Autor: E.G. Meyzis, Mai 1992

   Compile Instr. fuer DICE:                  execute cterm0
   dcc Terminal0.c -c -o t:Terminal0.o

   Link Instr. fuer DICE:                     execute lterm0
   dlink dlib:c.o t:Terminal0.o dlib:c.lib+
   dlib:amigas20.lib dlib:auto.lib dlib:x.o -o Terminal0
*/

#include <Devices/Serial.h>
#include <Dos/Dos.h>
#include <Dos/DosExtens.h>
#include <Exec/Exec.h>
#include <Intuition/Intuition.h>
#include <stdlib.h>
#include <stdio.h>

/* +++++++++  Definitionen  ++++++++++++
*/
#define TRUE  1   /* um mit dem Datentyp BOOL zu arbeiten */
#define FALSE 0

      /* zwei Definitionen fuer DOS-Fenster */

#define TxTitle "CON:0/0/500/100/ Terminal-Send "
#define RxTitle "CON:0/101/500/100/ Terminal-Receive "

      /* Texte zur Info bei Text-Senden/Empfangen */

#define Senden   "\nSende Textdatei\n\n"
#define Empfang  "\nwarte auf Textdatei\n\n"
#define Beendet  "\nÜbertragung beendet\n\n"

#define Unit0     0      /* eingebaute ser. Schnittstelle */
#define PLaenge 512      /* Pufferlaenge Datemuebertrag.  */

struct GetBlock             /* nur in GetWinPtr verwendet */
         {
          struct StandardPacket stdPkt;
          struct InfoData       info;
         };

/* +++++++++  Datentypen  ++++++++++++
*/
typedef char                BOOL;
typedef char                *CharPtr;
typedef struct IOExtSer     *IOExtSerPtr;
typedef struct FileHandle   *FileHandleBPtr;      /* BPTR */
typedef struct FileHandle   *FileHandlePtr;    /* Adresse */
typedef struct Window       *WindowPtr;
typedef struct GetBlock     *GetBlockPtr;
typedef struct MessagePort  *MsgPortPtr;
typedef struct IntuiMessage *IntuiMessagePtr;


APTR GetSysBase();    /* Adr ExecBase aus Adresse 4 holen */

void GetWinPointer (FileHandleBPtr *winHdl,
                    WindowPtr      *winPtr);
void MakeWindow(CharPtr        namePtr,
                WindowPtr      *winPtr,
                FileHandleBPtr *fileHdl);
BOOL WindowsAngelegt();

void SetBaudRate(IOExtSerPtr serPtr);
BOOL InitSerialDev(IOExtSerPtr serPtr, int cmnd);
BOOL SerialVorbereitet();
void SetTermChars(IOExtSerPtr serPtr);
void FlushSerialDevice();

void SendeTextDatei(IOExtSerPtr txReqPtr);
void EmpfangeTextDatei(IOExtSerPtr rxReqPtr);

void TerminalLoop();
void Aufraeumen();

/* +++++++++  globale Variablen  ++++++++++++
*/
struct ExecBase *execBasePtr;

/*
               Doppelt ausgelegte Datenstrukturen

                Sendeteil:         Empfangsteil:
*/
                /* Device zweimal oeffnen */
struct IOExtSer txRequest,         rxRequest;

                /* jedem Device seinen Puffer */
char            rxPuffer[PLaenge], txPuffer[PLaenge];

                /* getrennte Fenster fuer Ein-/Ausgaben */
FileHandleBPtr  txWin,             rxWin;
WindowPtr       txWinPtr,          rxWinPtr;



APTR GetSysBase() /* Adr ExecBase aus Adresse 4 holen */
{
 long *systemBasis;

 systemBasis = (APTR)4;
 return (APTR)*systemBasis;
}


/* Mit Dos.Open geoeffnete Fenster liefern nur einen Handle
   und leider nicht den WindowPtr; deshalb:
*/
void GetWinPointer (FileHandleBPtr *winHdl,
                    WindowPtr      *winPtr)
{
 MsgPortPtr  portPtr;
 GetBlockPtr pktPtr;

 FileHandlePtr wHdlPtr;

 *winPtr = NULL;                   /* negatives Vorurteil */
 if (IsInteractive(*winHdl))        /* Fenster vorhanden? */
   {
    wHdlPtr = (APTR)((long)*winHdl) << 2;  /* BPTR -> Adr */
    if (portPtr = (MsgPortPtr)CreateMsgPort())
      {
       /* Speicher fuer Dos.Packet anfordern*/

       pktPtr=(GetBlockPtr)AllocMem(sizeof(struct GetBlock),
                                    MEMF_CLEAR|MEMF_PUBLIC);
       if (pktPtr)           /* Dos.Packet initialisieren */
         {
          pktPtr->stdPkt.sp_Pkt.dp_Link =
                                     &pktPtr->stdPkt.sp_Msg;
          pktPtr->stdPkt.sp_Pkt.dp_Port = portPtr;
          pktPtr->stdPkt.sp_Pkt.dp_Type = ACTION_DISK_INFO;
          pktPtr->stdPkt.sp_Pkt.dp_Arg1 = &pktPtr->info >>2;
          pktPtr->stdPkt.sp_Msg.mn_Node.ln_Name =
                                     &pktPtr->stdPkt.sp_Pkt;
          pktPtr->stdPkt.sp_Msg.mn_Node.ln_Type= NT_MESSAGE;
          pktPtr->stdPkt.sp_Msg.mn_Length =
                                     sizeof(struct Message);

          PutMsg(wHdlPtr->fh_Type, pktPtr);/*und absenden */
          do
            WaitPort(portPtr);     /* auf Antwort von Dos */
          while (APTR)GetMsg(portPtr) != pktPtr;

          if (pktPtr->stdPkt.sp_Pkt.dp_Res1)/* Antwort ok */
             *winPtr = (APTR)pktPtr->info.id_VolumeNode;

           DeleteMsgPort(portPtr);
           FreeMem(pktPtr, sizeof(struct GetBlock));
         }
       else
         DeleteMsgPort(portPtr);
      }

   } /* Interactive */
}


/* Dos.Fenster anlegen und WindowPtr holen */

void MakeWindow(CharPtr        namePtr,
                WindowPtr      *winPtr,
                FileHandleBPtr *fileHdl)

{
  *fileHdl = (FileHandleBPtr)Open(namePtr, MODE_NEWFILE);
  if (*fileHdl)
    GetWinPointer(&(*fileHdl), &(*winPtr));
}


/* Beide Terminal-Fenster anlegen und bei Fehlschlag
   Ressourcen freigeben.
*/
BOOL WindowsAngelegt()
{
  BOOL angelegt;

  angelegt = FALSE;         /* zunaechst keines vorhanden */

  MakeWindow(TxTitle, &txWinPtr, &txWin);/* Sende-Fenster */
  if (txWinPtr)
    {
     angelegt = TRUE;

     /* um Tastatureingaben durch Intuition abzufangen */

     ModifyIDCMP(txWinPtr, IDCMP_RAWKEY|IDCMP_VANILLAKEY);

     MakeWindow(RxTitle, &rxWinPtr, &rxWin);/*Empf.-Fenst */
     if (rxWinPtr = NULL)
       {
        angelegt = FALSE;        /* Sendefenster aufgeben */
        Close(txWin);
        txWin = NULL;
       }
    }
  else                    /* WindowPtr nicht zu ermitteln */
    if (txWin)
       Close(txWin);                /* deshalb schliessen */
  return angelegt;
}

/* 3. Erweiterung Listing 3-3 hier einsetzen */

/* 4. Erweiterung Listing 3-4 hier einbauen */


/* Device-Struktur initialisieren und Device oeffnen */

BOOL InitSerialDev(IOExtSerPtr serPtr, int cmnd)
{
  MsgPortPtr  portPtr;
  BOOL        erfolg;

  erfolg = FALSE;
  portPtr = (MsgPortPtr)CreateMsgPort();   /* fuer Device */
  if (portPtr)
    {
     serPtr->io_SerFlags = SERF_SHARED; /* Mehrfachutzung */
     serPtr->IOSer.io_Length = 1;/* 1 Byte lesen/schreib. */
     serPtr->IOSer.io_Message.mn_ReplyPort = portPtr;

     OpenDevice(SERIALNAME, Unit0, serPtr, 0);
     if (serPtr->IOSer.io_Error != IOERR_OPENFAIL)
       {
        erfolg = TRUE;

        /* 3. Erweiterung Listing 3-3 hier aufrufen
           SetBaudRate(serPtr);
        */
        serPtr->IOSer.io_Command = cmnd;/* CMD_WRITE/READ */
       }
      else                     /* Device nicht zu oeffnen */
       {
        DeleteMsgPort
                    (serPtr->IOSer.io_Message.mn_ReplyPort);
        serPtr->IOSer.io_Message.mn_ReplyPort = NULL;
       }
    }
  return erfolg;
}


BOOL SerialVorbereitet() /* getrennte Devices als  Sende- */
{                        /* Empfangskanal oeffnen */
  BOOL vorbereitet;

  if (InitSerialDev(&txRequest, CMD_WRITE)) /* Sendekanal */
    {
     vorbereitet = TRUE;
     txRequest.IOSer.io_Data=txPuffer;/*f. Dateiuebertrag.*/

     if (InitSerialDev(&rxRequest, CMD_READ))/*Empf.-Kanal*/
       {
        rxRequest.IOSer.io_Data = rxPuffer; /* ext. Puffer*/
       }
     else                /* kein Empfangskanal zu oeffnen */
       {
        vorbereitet = FALSE;
        DeleteMsgPort
                  (txRequest.IOSer.io_Message.mn_ReplyPort);
        txRequest.IOSer.io_Message.mn_ReplyPort = NULL;
       }
    }
  else/*kein Kanal zu oeffnen; Device evtl exklusiv belegt*/
    vorbereitet = FALSE;

  return vorbereitet;
}


/* 5. Erweiterung Listing 3-5 hier einbauen */

/* 6. Erweiterung Listing 3-6 hier einbauen */

/* 6. Erweiterung Listing 3-6 */


void TerminalLoop()           /* Hauptschleife  des Prog. */
{
  IntuiMessagePtr inMsgPtr;  /* fuer IDCMP-Nachr. aus dem */
  long            klasse,                 /* Sendefenster */
                  done,
                  txSig, /*aus UserPort des Sendefensters */
                  rxSig,  /* aus Empfangs-Device-Struktur */
                  erwartSig, /* Kombination txSig | rxSig */
                  erhaltSig;       /* angekommene Signale */
  int             raw;             /* fuer Tastatur-Codes */

  txSig = 1 << txWinPtr->UserPort->mp_SigBit;
  rxSig = 1 <<
         rxRequest.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
  erwartSig = txSig | rxSig;

  SendIO(&(rxRequest));  /* async. aufs 1. Zeichen warten */
  for(;;)
    {
     erhaltSig = Wait(erwartSig);  /* Tastendruck/Empfang */
     if (txSig & erhaltSig) /*Taste im Sendefenster gedr. */
       {
        inMsgPtr = (IntuiMessagePtr)      /* Msg. abholen */
                   GetMsg(txWinPtr->UserPort);

        klasse = inMsgPtr->Class;   /* copy Msg teilweise */
        raw    = inMsgPtr->Code;

        ReplyMsg(inMsgPtr);            /* Msg beantworten */

        if (IDCMP_VANILLAKEY & klasse)   /* ASCII-Zeichen */
          {
           txPuffer[0] = raw;  /* Zeichen in Sende-Puffer */
           if (txPuffer[0] != 033) /* nicht ESC gedrueckt */
             {
              done = Write(txWin, txPuffer, 1);   /* Echo */
              DoIO(&txRequest);         /* Zeichen senden */
             }
           else                          /* ESC gedrueckt */
             break;                 /* Programm abbrechen */
          }
       else
         if (IDCMP_RAWKEY & klasse) /* z.B. F1 - F10 gedr.*/
           {
            if ((raw >= 80) && (raw <= 89))
              {
              /* Funktionstasten auswerten F1 - F10 */
               if (raw == 80)
                 {                       /* F1 */
                  /* 5. Erweiterung
                     Aufruf SendeTextDatei(txRequest);
                     aus Listing 3-5 hier einfuegen
                  */
                 }
                else
                  if (raw == 81)                    /* F2 */
                    {
                     /* 6. Erweiterung
                        Aufruf EmpfangeTextDatei(rxRequest);
                        aus Listing 3-6 hier einfuegen
                     */
                    }
              } /* raw >= 80*/
           } /* IDCMP_RAWKEY */
       } /* txSig */
     if (rxSig & erhaltSig)/* mind. 1 Zeich. im Empf-kanal*/
       {
        WaitIO(&rxRequest);             /* dieses abholen */
        if (rxRequest.IOSer.io_Error == 0)
           done = Write(rxWin, rxPuffer, 1);/*u. ausgeben */

        /* 2. Erweiterung gem Listing 3-2 hier einfuegen */

        SendIO(&rxRequest); /* aufs naechste Zeich. wart. */
       }
    } /* for */
    AbortIO(&rxRequest);   /* Auftrag an Device abbrechen */
    WaitIO(&rxRequest);
}

/* 1. Erweiterung  FlushSerialDevice;
   Listing 3-1 hier einbauen
*/

void Aufraeumen()
{
  DeleteMsgPort(rxRequest.IOSer.io_Message.mn_ReplyPort);
  CloseDevice(&rxRequest);
  DeleteMsgPort(txRequest.IOSer.io_Message.mn_ReplyPort);
  CloseDevice(&txRequest);
  Close(rxWin);
  Close(txWin);
  /* 1. Erweiterung
     Aufruf FlushSerialDevice aus
     Listing 3-1 hier vornehmen
  */
}

int main()
{
 BOOL done;

 execBasePtr = GetSysBase();   /* Adr ExecBase beschaffen */

 if (execBasePtr->LibNode.lib_Version >= 36) /* ja, OS 2  */
  {
   if (WindowsAngelegt() && SerialVorbereitet())
     {
      TerminalLoop();     /* Steuerschleife des Programms */
      Aufraeumen();
     }
  }
  return 0;
}
