#pragma option w32c
#pragma option ws=terminal.exe
#argc TRUE

#include "windows.h--"
#include "winbase.h--"
#include <wincon.h-->
#include <winerror.h-->
#include <advapi.h-->

char *comport="COM1";

void stdcall WRITESTR(dword str)
{
  _lwrite(hStdOut,str,lstrlenA(str));
}

void PrintError( LPCSTR str )
{
  WRITESTR(str);
  WRITESTR("\n");
}

DWORD ConInThread( HANDLE h )
OVERLAPPED ov;
HANDLE hconn;
BOOL quit;
char buf[10];
DWORD read;
DWORD write;
{
// starts a user input thread using the console
// takes console characters and sends them to the com port
  hconn = GetStdHandle(STD_INPUT_HANDLE);
  quit = FALSE;
  ZeroMemory(#ov,sizeof(ov));
  ov.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  IF(EAX == INVALID_HANDLE_VALUE) {
    PrintError("CreateEvent failed");
    SetCommMask(h,0);
    return 0;
  }
  SetConsoleMode(hconn,0);
  WRITESTR("press Esc to terminate\n");
  do {
    read = 0;
// wait for user to type something
    WaitForSingleObject(hconn,INFINITE);
// read the console buffer
    IF(!ReadConsole(hconn,#buf,sizeof(buf),#read,NULL)) {
      PrintError("ReadConsole failed");
      quit  = TRUE;
    }
    if(read) {
// check for Esc
      FOR(write=0;write<read;write++) {
// terminate when Esc pressed
        ESI=write;
        IF( buf[ESI] == 0x1b ) {
          quit = TRUE;
          BREAK;
        }
      }
// send it to the com port
      IF(write) {
        IF( !WriteFile(h,#buf,write,#write,#ov) ) {
          IF(GetLastError() == ERROR_IO_PENDING) {
            IF( !GetOverlappedResult(h,#ov,#write,TRUE) ) {
              PrintError("GetOverlappedResult failed");
              quit = TRUE;
            }
          }
        }
        ELSE {
          PrintError("WriteFile failed");
          quit = TRUE;
        }
      }
    }
  } while(quit==0);
// tell terminal thread to quit
  SetCommMask(h,0);
  return 0;
}

void Terminal( HANDLE h )
DWORD mask;
DWORD id;
OVERLAPPED ov;
HANDLE hconn;
HANDLE hconin;
char buf[10];
DWORD read;
DWORD r;
{
  hconn = GetStdHandle(STD_OUTPUT_HANDLE);
  hconin = CreateThread(NULL,0,#ConInThread,h,0,#id);
  IF( hconin == INVALID_HANDLE_VALUE ) {
    PrintError("CreateThread failed");
    return;
  }
  CloseHandle(hconin);  // don't need this handle
  ZeroMemory(#ov,sizeof(ov));
// create event for overlapped I/O
  ov.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  IF(ov.hEvent == INVALID_HANDLE_VALUE) PrintError("CreateEvent failed");
// wait for received characters
  IF(SetCommMask(h,EV_RXCHAR)==0)PrintError("SetCommMask failed");
  for(;;) {
// get the event mask
    IF( !WaitCommEvent(h,#mask,#ov) ) {
      GetLastError();
      IF( EAX == ERROR_IO_PENDING ) {
        IF( !GetOverlappedResult(h,#ov,#r,TRUE) ) {
          PrintError("GetOverlappedResult failed");
          break;
        }
      }
      ELSE {
        PrintError("WaitCommEvent failed");
        break;
      }
    }
// if no event, then UI thread terminated with SetCommMask(h,0)
    IF( mask == 0 ) break;
    if( mask & EV_RXCHAR) {
      do {
        read = 0;
        IF( !ReadFile(h,#buf,sizeof(buf),#read,#ov) ) {
          IF( GetLastError() == ERROR_IO_PENDING ) {
            IF( !GetOverlappedResult(h,#ov,#read,TRUE) ) {
              PrintError("GetOverlappedResult failed");
              BREAK;
            }
          }
          ELSE {
            PrintError("ReadFile failed");
            BREAK;
          }
        }
        IF(read) WriteFile(hconn,#buf,read,#read,NULL);
      } while(read);
    }
    mask = 0;
  }
  CloseHandle(ov.hEvent);  // close the event
}

int main()
COMMTIMEOUTS cto;
DCB dcb;
HANDLE h;
{
      // open port for overlapped I/O
  IF(@PARAMCOUNT()>1){
    comport=@PARAMSTR(1);
  }
  h = CreateFile(comport,GENERIC_READ|GENERIC_WRITE,0,NULL,
    OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
  IF(h == INVALID_HANDLE_VALUE)PrintError("Failed to open port");
  else {
// set timeouts
    cto.ReadIntervalTimeout=2;
    cto.ReadTotalTimeoutMultiplier=1;
    cto.ReadTotalTimeoutConstant=1;
    cto.WriteTotalTimeoutMultiplier=0;
    cto.WriteTotalTimeoutConstant=0;
    IF(!SetCommTimeouts(h,#cto)) PrintError("SetCommTimeouts failed");
// set DCB
    ZeroMemory(#dcb,sizeof(dcb));
    dcb.DCBlength = sizeof(dcb);
    dcb.BaudRate = 9600;
    dcb.Flags=0b10000000010101;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    dcb.ByteSize = 8;
    IF(!SetCommState(h,#dcb)) PrintError("SetCommState failed");
// start terminal
    Terminal(h);
    CloseHandle(h);
  }
  return 0;
}
