home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 14
/
CD_ASCQ_14_0694.iso
/
maj
/
4151
/
smalterm.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1994-03-22
|
60KB
|
1,578 lines
/////////////////////////////////////////////////////////////////////////////
// S M A L T E R M -- D e m o D r i v e r f o r M C O M M //
// A s y n c R o u t i n e s //
// //
// //
// Mike Dumdei, 6 Holly Lane, Texarkana TX 75503 //
// North East Texas DataLink, 903 838-6713 //
// //
// (Link with comm_s.lib for external functions) //
// ztc smalterm.cpp comm.cpp comm_s.lib //
// bcc smalterm.cpp comm.cpp comm_s.lib //
// tcc smalterm.cpp comm.cpp comm_s.lib //
// cl smalterm.cpp comm.cpp /link comm_s //
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <process.h>
#include <stdarg.h>
#include <dos.h>
#include <bios.h>
#include <signal.h>
#include "comm.hpp" // header for AsyncPort class
#include "timer.hpp" // header for Timer class
#include "ansivid.hpp" // header for ANSI display functions
#define KEY_INT
#include "keys.h" // key definition header
#include "colors.h" // vid mode & color definition header
#include "extra.h" // extra functions header
#if defined(__TURBOC__) // Turbo C++
#define KBHIT bioskey(1)
#define KBREAD bioskey(0)
#else // Zortech C++, Microsoft C++
#define KBHIT _bios_keybrd(1)
#define KBREAD _bios_keybrd(0)
#endif
#ifndef __ZTC__
#include <io.h>
#define strcmpl stricmp
#endif
#define NO_INPUT (-1)
#define ESC_PRESSED (-2)
#define TIMED_OUT (-3)
#define NO_CARRIER (-4)
#define ERROR_LIMIT (-5)
#define GOT_CANNED (-6)
#define BLK_COMP_ERR (-7)
#define BLK_SEQ_ERR (-8)
#define CHECKSUM_ERR (-9)
#define TERMINAL 0
#define HOSTMODE 1 // hostmode isn't in here yet
#define SOH '\x01'
#define STX '\x02'
#define EOT '\x04'
#define ACK '\x06'
#define NAK '\x15'
#define CAN '\x18'
///////////////////////////////////////////////////////////////
// function declarations in order found in source (random) //
//:////////////////////////////////////////////////////////////
// void ControlBreakHandler(int sig);
int ProcessKeyPress();
void ProcessExtendedKey(int LongCh);
int ProcessAsyncChar();
int SaveScreen();
void RestoreScreen();
void VideoStatus(int saving);
void ClearScreen();
void ToggleLogFile();
void ToggleEcho();
void ToggleLineFeeds();
void ToggleDtr();
void DialNumber();
void DOS_Shell();
void DOS_Command();
void HostMode();
void MessageUpload();
void TXZMUpload();
void TXZMDownload();
void RunBatchFile(char *FileName);
void SendFile();
void ReceiveFile();
void ExitProgram();
int DisplayHelp();
void DisplayParameters();
void ChangeParameters();
int DrawBox(int Top, int Lft, int Height, int Length);
void RemoveBox();
int Prompt(char *Response, char *PromptMsg, int MaxResponse);
void HangUp();
int MonitoredDelay(int Ticks, int MonitorCarrier);
int WaitFor(char *String, int Ticks, int MonitorCarrier);
void vDisplay(char *Format, ...);
void vDisplay(int Color, char *Format, ...);
void vDisplay(int Row, int Col, char *Format, ...);
int PushScreen(int Row, int Col, int NbrRows, int NbrCols);
int PopScreen();
void ReceiveXmodem(char *FileName);
void SendXmodem(char *FileName, int BlockSize = 128);
void XmodemExitMsg(int rval);
int RxWithTimeOut(int Ticks);
void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
void SendCANs();
int WaitForBlockToEnd(int Ticks);
int GetHdrChar(int Ticks);
void TransferWindow(int Flag);
////////////////////////
// global variables //
////////////////////////
AsyncPort port(4096, 2048); // port with 4K transmit, 2K receive buffers
char port_name[5]; // ASCII "COM1" or "COM2"
int port_number; // assigned number COM1==0, COM2==1
int Mode; // identifies to subroutines HOST or TERMINAL
FILE *LogFile = NULL; // handle for log file
char DialString[40]; // storage for dial string
int LineFeeds = 0; // supply LineFeeds flag
int Echo = 0; // Echo on flag
int Dtr = 1; // DTR signal level flag
int cyn, hred, grn, hgrn; // video color variables to enable support
int wht, hblu, ylw; // for either color or mono system
char vDispBuf[128]; // buffer for formatted display functions
char *ScreensArray[10]; // array of pointers for Push/Pop Screen
char ScreenIndex = 0; // index of current pointer in above array
/////////////////////////////////////////////////
// main //
//://////////////////////////////////////////////
int cdecl main (int argc, char *argv[])
{
char params[10], *pc;
int got_port = 0, return_value, i;
////////////////////////////////////
// process command line arguments //
////////////////////////////////////
*params = '\0'; // preset params to nul string
for (i = 1; i < argc; i++)
{
pc = argv[i]; // pointer to argument
if (*pc == '-' || *pc == '/')
++pc; // skip any switch characters
if (got_port == 0)
{
if (strcmpl(argv[i], "COM1") == 0) // check for "COM1"
got_port = 1;
else if (strcmpl(argv[i], "COM2") == 0) // check for "COM2"
got_port = 1, port_number = COM2;
}
if (isdigit(*pc) && strlen(pc) < 10) // check for parameter string
strupr(strcpy(params, argv[i]));
}
strcpy(port_name, (port_number == COM1) ? "COM1" : "COM2");
///////////////////////////
// initialize the video //
///////////////////////////
initvid(); // initialize lowlevel video variables
if (v_mode != MONO && v_mode != CO80)
setvmode(CO80); // reset video mode if necessary
if (v_mode == CO80)
{ // attributes to use if color system
cyn = CYN, hred = H_RED, grn = GRN, hgrn = H_GRN,
wht = WHT, hblu = H_BLU, ylw = YLW;
}
else
{ // attributes to use if mono system
cyn = WHT, hred = H_WHT, grn = WHT, hgrn = H_WHT,
wht = WHT, hblu = H_WHT, ylw = H_WHT;
}
SaveScreen();
/////////////////////////////
// display sign on message //
/////////////////////////////
Display(hred, "SMALTERM - Sample driver for MCOMM library functions\n");
Display(grn, " Mike Dumdei, 6 Holly Lane, Texarkana TX 75503\n");
Display(" North East Texas DataLink 903 838-6713\n");
Display(hred, "Usage: smalterm {COM1 | COM2} {param str EX: 2400N81}\n\n");
v_color = cyn;
//////////////////////////
// open the serial port //
//////////////////////////
return_value = port.Open(port_number, params);
if (return_value != 0) // if port open failed
{
vDisplay(
"SMALTERM: Can't open %s, Parameters: %s, Error code: %04X\n",
port_name, (*params) ? params : "default", return_value);
exit(return_value);
} // if port open succeeded
vDisplay("SMALTERM: %s -- PORT OPENED\n\n", port_name);
Display("Press F1 for HELP screen\n");
//////////////////////////
// miscellaneous set up //
//////////////////////////
tickhookset(1); // enable master tick in Timer class
/* ControlBreakHandler(SIGINT); // initialize control-break handler */
SetWindowSize(0, 0, 23, 79); // set window to protect status line
Fill(24, 0, '═', cyn, 80); // display status line
Display(24, 60, hgrn, port_name);
DisplayParameters();
Display(24, 10, hgrn, "DTR");
*DialString = '\0'; // clear out dial string
Mode = TERMINAL; // terminal, e.g. not host mode
///////////////////////////////////
////// MAIN PROGRAM LOOP //////
///////////////////////////////////
while (1)
{
if (KBHIT) // process keyboard character if a
ProcessKeyPress(); // key was pressed
if (port.RxLevel() != 0) // process character from serial
ProcessAsyncChar(); // port if one is available
}
#if (!__TURBOC__ && !__ZTC__)
return 0; // suppresses MSC compile warning
#endif
}
/*
/////////////////////////////////////////////////
// ControlBreakHandler //
//://////////////////////////////////////////////
void ControlBreakHandler(int sig)
{
signal(SIGINT, ControlBreakHandler);
}
*/
/////////////////////////////////////////////////
// ProcessKeyPress //
//://////////////////////////////////////////////
int ProcessKeyPress()
{
int LongCh = KBREAD; // scan code + character
char Ch = (char)LongCh; // character without scan code
if (Ch != '\0') // if normal key
{
port.Tx(Ch); // send it out the serial port
if (Echo && Ch != ESC) // if echo on & not ESC (don't want
{ // to start an ANSI sequence),
VideoStatus(1); // save video status
if (Ch == '\r' && LineFeeds)
Ch = '\n'; // make it a LF if CR & LF toggle
Display(Ch); // display the char,
if (LogFile) // and write it the log file if
fputc(Ch, LogFile); // its open
VideoStatus(0); // restore video status
}
}
else // extended key, e.g. F1, ALT_X, etc,
ProcessExtendedKey(LongCh);
return (LongCh); // return the key pressed
}
/////////////////////////////////////////////////
// ProcessExtendedKey //
//://////////////////////////////////////////////
void ProcessExtendedKey(int LongCh)
{
static struct JUMP_TABLE
{
int KeyCode;
void (*Function)();
}
TERMINAL_Table[] = {
{ ALT_C, ClearScreen }, { ALT_E, ToggleEcho },
{ ALT_X, ExitProgram }, { ALT_S, DOS_Shell },
{ ALT_D, DialNumber }, { PGUP, SendFile },
{ PGDN, ReceiveFile }, { ALT_H, HangUp },
{ ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
{ F10, HostMode }, { ALT_P, ChangeParameters },
{ F4, DOS_Command }, { F3, MessageUpload },
{ F5, TXZMUpload }, { F6, TXZMDownload },
{ 0, NULL } },
HOSTMODE_Table[] = {
{ ALT_C, ClearScreen }, { ALT_E, ToggleEcho },
{ ALT_S, DOS_Shell }, { ALT_H, HangUp },
{ ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
{ ALT_P, ChangeParameters },{ F4, DOS_Command },
{ F3, MessageUpload }, { F5, TXZMUpload },
{ F6, TXZMDownload }, { 0, NULL } },
*JumpTableList[] = { TERMINAL_Table, HOSTMODE_Table };
JUMP_TABLE *pjt = JumpTableList[Mode];
if (LongCh == F1)
LongCh = DisplayHelp(); // run Help if F1 was pressed
while (pjt->KeyCode != LongCh && pjt->KeyCode)
++pjt; // look up the key in the table
if (pjt->KeyCode)
pjt->Function(); // if key found, run the function
}
/////////////////////////////////////////////////
// ProcessAsyncChar //
//://////////////////////////////////////////////
int ProcessAsyncChar()
{
static int ToggleHost = 0;
char Ch = (char)port.Rx(); // read the character
if (Ch == '\r' && LineFeeds)
Ch = '\n'; // translate to LF if supplying LF's
if (Ch != '~') // using ~ to remotely toggle host
ToggleHost = 0; // mode, reset counter if not ~
else
{
if (++ToggleHost >= 10) // seq of 10 ~'s toggles host mode
{
HostMode();
ToggleHost = 0; // reset counter on HostMode exit
return 0;
}
}
Display(Ch); // display the character
if (LogFile && v_ansiseq == '\0')
fputc(Ch, LogFile); // write to log file if its on
return ((int)Ch & 0xff); // return received char
}
/////////////////////////////////////////////////
// SaveScreen //
//://////////////////////////////////////////////
int SaveScreen()
{
if (PushScreen(0, 0, 25, 80) != 0) // move screen to memory buffer
return 0; // return failed if no memory
VideoStatus(1); // save video color & ANSI status
SetWindowSize(0, 0, 24, 79);
v_color = wht;
ClearWindow(); // reset window size, clear screen
return 1; // return success
}
/////////////////////////////////////////////////
// RestoreScreen //
//://////////////////////////////////////////////
void RestoreScreen()
{
PopScreen(); // restore screen
VideoStatus(0); // restore v_color & v_ansiseq
SetWindowSize(0, 0, 23, 79); // reset the window size
}
/////////////////////////////////////////////////
// VideoStatus //
//://////////////////////////////////////////////
void VideoStatus(int saving)
{
static char savedcolor[4], savedseq[4], vsi = 0;
if (saving)
savedcolor[vsi] = v_color, savedseq[vsi++] = v_ansiseq,
v_ansiseq = '\0';
else
v_color = savedcolor[--vsi], v_ansiseq = savedseq[vsi];
}
/////////////////////////////////////////////////
// ClearScreen //
//://////////////////////////////////////////////
void ClearScreen()
{
v_color = cyn; // reset screen color
SetWindowSize(0, 0, 23, 79); // reset the window size
ClearWindow(); // clear the current window
}
/////////////////////////////////////////////////
// ToggleLogFile //
//://////////////////////////////////////////////
void ToggleLogFile()
{
if (LogFile == NULL) // if it's off, turn it on
{
if ((LogFile = fopen("ST.LOG", "ab")) != NULL)
Display(24, 25, hgrn, "LOG");
}
else // if it's on, turn it off
{
fclose(LogFile);
LogFile = NULL; // a NULL FILE * shows log is off
Display(24, 25, cyn, "═══");
}
}
/////////////////////////////////////////////////
// ToggleEcho //
//://////////////////////////////////////////////
void ToggleEcho()
{
Echo ^= 1; // switch state, update status line
Display(24, 15, (Echo) ? hgrn : cyn, (Echo) ? "ECHO" : "════");
}
/////////////////////////////////////////////////
// ToggleLineFeeds //
//://////////////////////////////////////////////
void ToggleLineFeeds()
{
LineFeeds ^= 1; // switch state, update status line
Display(24, 21, (LineFeeds) ? hgrn : cyn, (LineFeeds) ? "LF" : "══");
}
/////////////////////////////////////////////////
// ToggleDtr //
//://////////////////////////////////////////////
void ToggleDtr()
{
Dtr ^= 1; // switch state, update status line
port.Dtr(Dtr);
Display(24, 10, (Dtr) ? hgrn : (hred | BLNK), "DTR");
}
/////////////////////////////////////////////////
// DialNumber //
//://////////////////////////////////////////////
void DialNumber()
{
char Response[25], *pc = Response;
if (Prompt(pc, "Enter number (p###=Pulse, r=Redial): ", 24) != 0)
return; // get number, exit if ESC pressed
if (isalpha(*pc)) // look for 'P' or 'R'
{
*pc = toupper(*pc);
if (*pc == 'R' && *DialString != '\0')
{
port.Tx(DialString); // redial command
return;
}
else if (*pc != 'P')
return; // invalid response
}
if (*pc == 'P')
sprintf(DialString, "%s%s\r", "ATDP", &pc[1]);
else
sprintf(DialString, "%s%s\r", "ATDT", pc);
port.Tx(DialString);
}
/////////////////////////////////////////////////
// DOS_Shell //
//://////////////////////////////////////////////
void DOS_Shell()
{
char *comspec;
comspec = getenv("COMSPEC"); // get command interpreter name
if (comspec == NULL)
comspec = "COMMAND.COM";
if (SaveScreen() == 0) // save the current screen
return; // exit if save failed
Display(hred, "Type EXIT to return to SMALTERM.");
spawnlp(0, comspec, comspec, NULL); // run command interpreter
RestoreScreen(); // restore the original screen
}
/////////////////////////////////////////////////
// DOS_Command //
//://////////////////////////////////////////////
void DOS_Command()
{
char buf[48];
if (Prompt(buf, "Enter DOS command: ", 46) != 0)
return; // get the command
if (SaveScreen() == 0) // save the current screen
return; // exit if save failed
system(buf); // execute command
RestoreScreen(); // restore the original screen
}
/////////////////////////////////////////////////
// HostMode //
//://////////////////////////////////////////////
void HostMode()
{
// do this later
}
/////////////////////////////////////////////////
// MessageUpload //
//://////////////////////////////////////////////
void MessageUpload()
{
char buf[81];
FILE *fh;
char *pc;
if (Prompt(buf, "Enter ASCII filename: ", 46) != 0)
return; // get the file name
if ((fh = fopen(buf, "rt")) == NULL)
{
Prompt(buf, "File open error, Press ENTER\a", 0);
return; // abort if can't open the file
}
port.RxFlush(); // flush the port
port.TxFlush();
while (fgets(buf, 80, fh)) // while more is left
{
if (*buf == '\n') // change blank lines to SPC,CRs
strcpy(buf, " \r");
else if ((pc = strchr(buf, '\n')) != NULL)
*pc = '\r'; // change LFs to simulate ENTER key
for (pc = buf; *pc; pc++)
{
while (!port.TxEmpty()) // send a char at a time for looks
{
if (KBHIT && (KBREAD == X_ESC))
{ // monitor for upload aborted
fclose(fh);
return;
}
while (port.RxLevel() != 0)
ProcessAsyncChar();
}
port.Tx(*pc);
}
}
fclose(fh); // close the file
}
/////////////////////////////////////////////////
// TXZMUpload //
//://////////////////////////////////////////////
void TXZMUpload()
{
RunBatchFile("SND.BAT");
}
/////////////////////////////////////////////////
// TXZMDownload //
//://////////////////////////////////////////////
void TXZMDownload()
{
RunBatchFile("RCV.BAT");
}
/////////////////////////////////////////////////
// RunBatchFile //
//://////////////////////////////////////////////
void RunBatchFile(char *FileName)
{
char lbuf[50], *pc = lbuf;
strcpy(pc, FileName);
pc += strlen(pc);
*pc++ = ' '; // copy filename to local buffer
if (Prompt(pc, "Enter batch parameters -> ", 40) == ESC_PRESSED)
return; // add on any args, return if ESC
if (SaveScreen() == 0) // save current screen
return; // return if save failed
vDisplay(hred, "Executing: %s\n\n", lbuf);
system(lbuf); // run the batch file
RestoreScreen(); // restore the screen
}
/////////////////////////////////////////////////
// SendFile //
//://////////////////////////////////////////////
void SendFile()
{
char buf[48], *pc;
if (Prompt(buf, "Enter Filename{,k}: ", 46) != 0)
return; // get the file name
pc = strchr(buf, ','); // check if Xmodem-1k selected
if (pc && (strchr(pc, 'k') || strchr(pc, 'K')))
{
*pc = '\0';
SendXmodem(buf, 1024);
}
else // send the file
SendXmodem(buf);
}
/////////////////////////////////////////////////
// ReceiveFile //
//://////////////////////////////////////////////
void ReceiveFile()
{
char buf[48], *pc;
if (Prompt(buf, "Enter Filename: ", 46) != 0)
return; // get the file name
ReceiveXmodem(buf); // receive the file
}
/////////////////////////////////////////////////
// ExitProgram //
//://////////////////////////////////////////////
void ExitProgram()
{
char buf[2];
switch(Prompt(buf, "Exit SmalTerm (Y, n)? ", 1))
{ // make sure they want to do this
case ESC_PRESSED:
return; // return if ESC
default:
if (*buf != 'y' && *buf != 'Y')
return; // if not 'Yes', then return
case NO_INPUT: // 'Enter key' is a default 'yes'
break;
}
RestoreScreen(); // replace the original screen
tickhookset(0); // remove the interrupt 1C hook
exit(0); // leave the program
}
/////////////////////////////////////////////////
// DisplayHelp //
//://////////////////////////////////////////////
int DisplayHelp() // interim help function
{
int LongCh;
static char *HelpScrn = "\
F1 - Help ALT C - Clear Screen\n\
F2 - Toggle Log File ALT D - Dial Number\n\
F3 - BBS Message Upload ALT E - Toggle Echo\n\
F4 - Execute DOS Command ALT H - Hang Up\n\
F5 - TXZM Upload SND.BAT ALT L - Toggle Line Feeds\n\
F6 - TXZM Download RCV.BAT ALT P - Change Parameters\n\
F10 - Host Mode (not there) ALT S - DOS Shell\n\
PGUP - Xmodem Send ALT X - Exit Program\n\
PGDN - Xmodem Receive << Press ESC or Command >>";
DrawBox(5, 10, 12, 60);
SetWindowSize(6, 11, 18, 69);
v_color = ylw;
Display(6, 12, "SMALTERM COMMANDS:\n");
Display(wht, HelpScrn);
LongCh = KBREAD;
RemoveBox();
SetWindowSize(0, 0, 23, 79);
return LongCh;
}
/////////////////////////////////////////////////
// DisplayParameters //
//://////////////////////////////////////////////
void DisplayParameters()
{
Fill(24, 65, '═', cyn, 10);
Display(24, 65, hgrn, port.Params());
}
/////////////////////////////////////////////////
// ChangeParameters //
//://////////////////////////////////////////////
void ChangeParameters()
{
char buf[12];
if (Prompt(buf, "Enter parameters (Ex: 2400N81) -> ", 10) != 0)
return; // get the new parameters
if (port.Params(strupr(buf)) == 0)
DisplayParameters(); // display updated params if success,
else // else display error message
Prompt(buf, "Invalid parameters, Press ENTER to continue\a", 0);
}
/////////////////////////////////////////////////
// DrawBox //
//://////////////////////////////////////////////
int DrawBox(int Top, int Lft, int Height, int Length)
{
static char BoxChar[] = { '╔', '╗', '╚', '╝', '═', '║' };
int Btm, Rgt;
if (PushScreen(Top, Lft, Height, Length) != 0)
return 0; // save area for box, return if failed
Btm = Top + Height - 1;
Rgt = Lft + Length - 1;
VideoStatus(1); // save video variables
v_color = wht;
SetWindowSize(Top, Lft, Btm, Rgt);
ClearWindow(); // clear the area for the box
SetWindowSize(0, 0, 23, 79);
v_color = hblu; // draw the box
Fill(Top, Lft, BoxChar[4], hblu, Length);
Fill(Btm, Lft, BoxChar[4], hblu, Length);
Fill(Top, Rgt, BoxChar[5], hblu, Height, 0);
Fill(Top, Lft, BoxChar[5], hblu, Height, 0);
Display(Top, Lft, BoxChar[0]);
Display(Top, Rgt, BoxChar[1]);
Display(Btm, Lft, BoxChar[2]);
Display(Btm, Rgt, BoxChar[3]);
return 1;
}
/////////////////////////////////////////////////
// RemoveBox //
//://////////////////////////////////////////////
void RemoveBox()
{
PopScreen(); // restore screen
VideoStatus(0); // reset v_color & v_ansiseq
}
/////////////////////////////////////////////////
// Prompt //
//://////////////////////////////////////////////
int Prompt(char *Response, char *PromptMsg, int MaxResponse)
{
int Lft, Length;
char *pc, *end, ch;
Length = strlen(PromptMsg) + MaxResponse + 6;
Lft = (80 - Length) / 2; // calculate box size required
if (DrawBox(9, Lft, 5, Length) == 0) // draw the box
return ESC_PRESSED;
v_color = hred;
Display(11, Lft + 3, PromptMsg); // display prompt
v_color = wht;
pc = Response, end = pc + MaxResponse;
while ((ch = (char)KBREAD) != '\r' && ch != ESC)
{ // get the response
if (ch == '\b')
{ // backspace key
if (pc > Response)
--pc, Display(ch);
continue;
}
if (pc != end && isprint((int)ch & 0xff))
{ // good character
Display(ch), *pc++ = ch;
continue;
}
Display('\a'); // illegal character
}
*pc = '\0'; // terminate the string
RemoveBox();
if (ch == ESC)
return ESC_PRESSED; // return something
if (*Response == '\0')
return NO_INPUT;
return 0;
}
/////////////////////////////////////////////////
// HangUp //
//://////////////////////////////////////////////
void HangUp()
{
ToggleDtr(); // drop DTR
port.TxFlush();
port.RxFlush(); // flush the buffers
if (MonitoredDelay(24, 1) == 0) // wait a while for carrier
{ // to drop
port.Tx("+++"); // if dropping Dtr didn't
if (MonitoredDelay(18, 1) == 0) // work try modem command
{
port.Tx("ATH0\r");
WaitFor("OK", 54, 1); // 3 secs, monitor carrier
}
}
ToggleDtr();
}
/////////////////////////////////////////////////
// MonitoredDelay //
//://////////////////////////////////////////////
int MonitoredDelay(int Ticks, int MonitorCarrier)
{
int LongCh;
Timer t(Ticks); // start the delay
while (!t.Expired())
{
if ((LongCh = KBHIT) != 0) // check the keyboard
{
if (LongCh == X_ESC)
{
KBREAD;
return ESC_PRESSED; // return if ESC pressed
}
ProcessKeyPress(); // process key if not ESC
}
if (MonitorCarrier && !port.Carrier())
return NO_CARRIER;
if (port.RxLevel() != 0) // check the async port
ProcessAsyncChar();
}
return 0;
}
/////////////////////////////////////////////////
// WaitFor //
//://////////////////////////////////////////////
int WaitFor(char *String, int Ticks, int MonitorCarrier)
{
char ch, *pc, *buf, *end;
int MatchLen, LongCh, rval;
if ((MatchLen = strlen(String)) == 0)
return (0); // return if nothing to wait for
buf = new char[MatchLen]; // allocate & clear incoming chars buf
memset(buf, '\0', MatchLen);
pc = buf - 1;
end = pc + MatchLen; // preset pointers
Timer t(Ticks); // start a timer for 'Ticks' duration
while (1)
{
while (port.RxLevel() != 0) // check for characters in the port
{
ch = ProcessAsyncChar();
if (pc != end)
{ // if don't have MatchLen chars yet
*++pc = ch;
if (pc != end)
continue;
}
else // have MatchLen chars
{
memmove(buf, &buf[1], MatchLen);
*pc = ch; // put this char in the match buffer
} // and check for a match
if (*buf == *String && !memicmp(buf, String, MatchLen))
{
delete buf; // if found wait for string
return 0;
}
}
if ((LongCh = KBHIT) != 0) // if got a keyboard entry
{
if (LongCh == X_ESC)
{
KBREAD;
rval = ESC_PRESSED; // return if it was ESC
break;
}
ProcessKeyPress(); // else process key
}
if (t.Expired()) // if max wait time has elapsed
{
rval = ESC_PRESSED;
break;
}
if (MonitorCarrier && !port.Carrier())
{ // if lost carrier & care
rval = NO_CARRIER;
break;
}
}
delete buf;
return rval;
}
/////////////////////////////////////////////////
// vDisplay (1 of 3) //
//://////////////////////////////////////////////
void vDisplay(char *Format, ...)
{
va_list arg_ptr;
va_start(arg_ptr, Format);
vsprintf(vDispBuf, Format, arg_ptr);
Display(vDispBuf);
}
/////////////////////////////////////////////////
// vDisplay (2 of 3) //
//://////////////////////////////////////////////
void vDisplay(int Color, char *Format, ...)
{
va_list arg_ptr;
va_start(arg_ptr, Format);
vsprintf(vDispBuf, Format, arg_ptr);
Display(Color, vDispBuf);
}
/////////////////////////////////////////////////
// vDisplay (3 of 3) //
//://////////////////////////////////////////////
void vDisplay(int Row, int Col, char *Format, ...)
{
va_list arg_ptr;
va_start(arg_ptr, Format);
vsprintf(vDispBuf, Format, arg_ptr);
Display(Row, Col, vDispBuf);
}
/////////////////////////////////////////////////
// PushScreen //
//://////////////////////////////////////////////
int PushScreen(int Row, int Col, int NbrRows, int NbrCols)
{
if (ScreenIndex == 10) // return if ScreensArray is full
return (-1);
if ((ScreensArray[ScreenIndex] = new char[2*NbrRows*NbrCols+16]) == NULL)
return (-2); // return if can't alloc screen buffer
pu_scrnd(Row, Col, NbrRows, NbrCols, ScreensArray[ScreenIndex]);
++ScreenIndex; // increment the index
return 0;
}
/////////////////////////////////////////////////
// PopScreen //
//://////////////////////////////////////////////
int PopScreen()
{
if (ScreenIndex == 0)
return (-1); // return if no screens pushed
--ScreenIndex;
po_scrnd(ScreensArray[ScreenIndex]); // call low level restore screen
delete ScreensArray[ScreenIndex]; // release allocated memory
return 0;
}
/////////////////////////////////////////////////
// ReceiveXmodem //
//://////////////////////////////////////////////
void ReceiveXmodem(char *FileName)
{
FILE *fh;
char *pc, *buf, *end;
/////////////////////////////////////////////
// alloc memory, open file, display info //
/////////////////////////////////////////////
if ((buf = new char[1024 + 10]) == NULL) // alloc enough for 1K blocks
{
Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
return; // abort if can't alloc memory
}
if ((fh = fopen(FileName, "rb")) != NULL)
{
Prompt((char *)&buf, "File exists, Press ENTER\a", 0);
delete buf;
return; // abort if file exists
}
if ((fh = fopen(FileName, "wb")) == NULL)
{
Prompt((char *)&buf, "File creation error, Press ENTER\a", 0);
delete buf;
return; // abort if can't open output file
}
///////////////////////////////
// pull up transfer window //
///////////////////////////////
TransferWindow(-1); // display receive transfer window
Display(1, 48, strupr(FileName));
//////////////////////////////////////////////////////
// set xflow off, params to N81, ignore bad chars //
//////////////////////////////////////////////////////
char save_xoff, save_flushbad, save_params[10];
SetXmodemMode(save_xoff, save_flushbad, save_params);
//////////////////////////////
// intitiate the transfer //
//////////////////////////////
static int BlockSizeAry[] = { 0, 128, 1024 }; // nul, SOH, STX
int rval, LongCh, TimeOuts, BlockSize, UseCrc;
int i, BlockNbr, RxdBlockNbr, Checksum, NeedHdrChar, ConsecErrors;
long BytesTransferred = 0L;
TimeOuts = 0;
rval = 1;
while (rval == 1)
{
port.Tx((TimeOuts < 6) ? 'C' : NAK); // try CRC Xmodem first, if
switch (LongCh = GetHdrChar(90)) // no response try checksum
{
case SOH: // 128 byte block header
case STX: // 1K byte block header
BlockSize = BlockSizeAry[LongCh];
rval = 0;
break;
case TIMED_OUT: // no response
if (++TimeOuts > 10)
rval = TIMED_OUT;
break;
default: // fatal error
rval = LongCh;
break;
}
}
UseCrc = (TimeOuts < 5) ? 1 : 0; // set type of checksum to use
BlockNbr = 1; // initial block number
NeedHdrChar = TimeOuts = ConsecErrors = 0;
while (!rval)
{
LongCh = 0;
while (!LongCh) // while good blocks
{
////////////////////////////////
// get the next header char //
////////////////////////////////
if (NeedHdrChar) // if haven't already got SOH
{
LongCh = GetHdrChar(180); // wait up to 10 secs
if ((char)LongCh != SOH && (char)LongCh != STX)
break;
TimeOuts = 0;
BlockSize = BlockSizeAry[LongCh];
}
else
NeedHdrChar = 1;
////////////////////////////
// get the block number //
////////////////////////////
if ((LongCh = RxWithTimeOut(90)) < 0) // 90 == 5 secs
break;
RxdBlockNbr = LongCh;
if ((LongCh = RxWithTimeOut(90)) < 0)
break;
if (LongCh + RxdBlockNbr != 0xff)
{ // block complement error
LongCh = BLK_COMP_ERR;
break;
}
if ((char)RxdBlockNbr != (char)BlockNbr)
{ // not expected block number
LongCh = BLK_SEQ_ERR;
break;
}
////////////////////////
// receive the data //
////////////////////////
Checksum = 0;
pc = buf, end = pc + BlockSize; // initialize variables
while (pc < end)
{
if ((LongCh = RxWithTimeOut(90)) < 0)
break;
*pc++ = (char)LongCh; // read and store data
if (UseCrc) // update crc if using crc
Checksum = update_crc(Checksum, (char)LongCh);
else
Checksum += LongCh; // else update simple checksum
}
if (pc != end) // if an error occurred while
break; // receiving block data
///////////////////////////////
// receive the check value //
///////////////////////////////
if ((LongCh = RxWithTimeOut(90)) < 0)
break; // high byte of crc or the checksum
if (UseCrc)
{
if ((Checksum ^= (LongCh << 8)) & 0xff00)
{ // crc didn't match, breaking here
LongCh = CHECKSUM_ERR; // makes error recovery faster on
break; // short blocks
}
if ((LongCh = RxWithTimeOut(90)) < 0)
break; // low byte of crc
}
if ((LongCh ^= Checksum) != 0)
{ // crc or checksum didn't match
LongCh = CHECKSUM_ERR;
break;
}
port.Tx(ACK); // good block, send an ACK
fwrite(buf, 1, BlockSize, fh); // update the file
++BlockNbr; // advance the blocknbr
BytesTransferred += (long)BlockSize;
vDisplay(3, 48, "%ld", BytesTransferred);
ConsecErrors = 0; // reset consecutive error count
}
///////////////////////////////
// errors and EOT handling //
///////////////////////////////
if (LongCh == EOT) // end of file indicator
{
for (i = 0; i < 3; i++)
{
port.Tx(NAK); // try to confirm real EOT
if ((char)(LongCh = GetHdrChar(90)) == EOT)
{
port.Tx(ACK); // if got 2nd EOT after NAKing
rval = 1; // 1st one, it must be real EOT
break;
}
if ((char)LongCh == SOH // if got an SOH or STX, 1st EOT
|| (char)LongCh == STX // must have been false
|| (LongCh < 0 && LongCh != TIMED_OUT))
break; // if < 0, fatal error occurred
}
if (LongCh == TIMED_OUT) // got an EOT but after 3 attempts
rval = 2; // to get confirmation, timed out,
if (rval) // so exit at different level
break;
if ((char)LongCh == SOH || (char)LongCh == STX)
{ // got EOT followed by
Display(5, 48, "False EOT"); // valid start of block
BlockSize = BlockSizeAry[LongCh]; // char, try to get
NeedHdrChar = 0; // another block
continue;
}
}
if (++ConsecErrors == 10) // exit after 10 errors
LongCh = ERROR_LIMIT;
vDisplay(4, 48, "%d", ConsecErrors);
switch (LongCh) // switch on error code
{
case BLK_COMP_ERR:
Display(5, 48, "Bad Block Complement"); // complement error
if ((rval = WaitForBlockToEnd(18)) != 0) // wait for port to dry
break; // up, send a NAK, and
port.Tx(NAK); // and try again
break;
case BLK_SEQ_ERR:
Display("Bad Block Sequence ");
if ((char)RxdBlockNbr != (char)(BlockNbr - 1)
|| (LongCh = WaitForBlockToEnd(18)) != 0)
{ // sequence error
rval = LongCh; // if wrong seq and not
break; // previous seq either,
} // then have fatal error.
port.Tx(ACK); // if got repeat block,
continue; // ACK it and try again
case CHECKSUM_ERR:
Display(5, 48, (UseCrc) ? // bad crc or checksum
"Block Crc Error " :
"Block Checksum Error");
if ((rval = WaitForBlockToEnd(4)) != 0) // wait for about 1/4
break; // second of silence,
port.Tx(NAK); // then NAK block and
break; // try again
case TIMED_OUT:
Display(5, 48, // remote not answering
"Timed Out ");
port.Tx(NAK); // if no answer, send a NAK to
break; // try to get a response
default:
rval = LongCh; // CAN, ESC, lost carrier, ...
break; // set exit code and quit
}
}
if (rval == ESC_PRESSED) // send CANs if ESC pressed
{
Display(5, 48, "Aborting File Transfer ...");
WaitForBlockToEnd(18);
SendCANs();
}
fclose(fh); // close the file, free the
delete(buf); // memory, reset params and exit
ResetXmodemMode(save_xoff, save_flushbad, save_params);
TransferWindow(0);
XmodemExitMsg(rval); // display exit message
}
/////////////////////////////////////////////////
// SendXmodem //
//://////////////////////////////////////////////
void SendXmodem(char *FileName, int BlockSize)
{
FILE *fh;
char *pc, *buf, *end;
long FileLength;
/////////////////////////////////////////////
// alloc memory, open file, display info //
/////////////////////////////////////////////
#if defined(__ZTC__)
FileLength = filesize(FileName); // Zortech's filesize
#endif
if ((buf = new char[BlockSize + 10]) == NULL)
{
Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
return; // abort if can't alloc memory
}
if ((fh = fopen(FileName, "rb")) == NULL)
{
Prompt((char *)&buf, "File not found, Press ENTER\a", 0);
delete buf;
return; // abort if can't open file
}
#if !defined(__ZTC__)
FileLength = filelength(fileno(fh));
#endif
///////////////////////////////
// pull up transfer window //
///////////////////////////////
TransferWindow(1); // display transmit transfer window
Display(1, 48, strupr(FileName));
vDisplay(2, 48, "%ld", FileLength); // display filename and size
//////////////////////////////////////////////////////
// set xflow off, params to N81, ignore bad chars //
//////////////////////////////////////////////////////
char save_xoff, save_flushbad, save_params[10];
SetXmodemMode(save_xoff, save_flushbad, save_params);
/////////////////////////////
// wait for a 'C' or NAK //
/////////////////////////////
int LongCh, rval, CANsRxd, TimeOuts, UseCrc;
CANsRxd = TimeOuts = UseCrc = 0;
rval = 1;
while (rval == 1) // wait for something to happen
{
switch (LongCh = RxWithTimeOut(18))
{
case 'C': // CRC xmodem
UseCrc = 1;
rval = 0;
break;
case NAK: // simple checksum xmodem
rval = 0;
break;
case TIMED_OUT: // didn't get anything
if (++TimeOuts < 60)
continue;
rval = LongCh;
break;
case CAN: // remote sent control-X's
if (++CANsRxd < 2)
continue;
rval = GOT_CANNED;
break;
case NO_CARRIER: // lost the carrier
case ESC_PRESSED: // cancelled on this end
rval = LongCh;
break;
default:
CANsRxd = 0;
break;
}
}
////////////////////////////
// send the file blocks //
////////////////////////////
char BlockNbr = '\0', HdrChar = (BlockSize == 1024) ? STX : SOH;
int i, ConsecErrors, BadBlock, FirstAck, Checksum, ExtraNAK;
long BytesTransferred = 0L;
BadBlock = FirstAck = CANsRxd = 0;
while (!rval)
{
if (!BadBlock) // if last block was OK
{
++BlockNbr;
ConsecErrors = 0;
i = fread(buf, 1, BlockSize, fh); // read a block in
if (i == 0) // if end of file
{
rval = 1; // exit value if EOT gets ACKd
while (1)
{
port.Tx(EOT);
if ((char)RxWithTimeOut(18) == ACK)
break;
if (i++ == 4)
{
rval = 2; // exit value if EOT doesn't get ACKd
break;
}
}
break;
}
if (i < BlockSize) // pad last block with CTL-Z's
memset(&buf[i], '\x1a', BlockSize - i);
}
else // had a bad block
{
if (++ConsecErrors == 10)
{ // 10 consecutive errors, abort
rval = ERROR_LIMIT;
continue;
}
vDisplay(4, 48, "%d", ConsecErrors);
vDisplay(5, 48, "Got NAK for block %d", BlockNbr);
if (!FirstAck) // if not any good blocks yet
{
if (BlockSize == 1024 && ConsecErrors == 2)
{
BlockSize = 128; // Xmodem-1k isn't working,
BadBlock = 0; // try 128 byte blocks
BlockNbr = '\0';
HdrChar = SOH;
rewind(fh);
continue;
}
if (UseCrc && ConsecErrors == 8)
UseCrc = 0; // last ditch effort, try checksum
}
}
port.Tx(HdrChar); // send block header
port.Tx(BlockNbr);
port.Tx(~BlockNbr);
port.Tx(buf, BlockSize); // send file data
if (UseCrc)
{ // send the CRC
Checksum = calc_crc(buf, BlockSize);
port.Tx((char)(Checksum >> 8));
port.Tx((char)Checksum);
}
else
{ // if not CRC, send simple checksum
Checksum = 0;
pc = buf, end = pc + BlockSize;
while (pc < end)
Checksum += (int)*pc++;
port.Tx(Checksum);
}
////////////////////
// wait for ACK //
////////////////////
ExtraNAK = 0; //---------------------------//
while (!rval && !port.TxEmpty()) // while block is being sent //
{ // //
if (port.RxLevel() != 0) // if something comes in //
{ // //
LongCh = port.Rx(); // get whatever it is //
//---------------------------//
//--------------------------------------------------//
// If it's an ACK ignore it. It may be due to a //
// duplicate packet error but it is safer to make //
// the receiver timeout and send another ACK after //
// the packet has been sent. //
// If it's a CAN, ignore that also and make them //
// resend them at the end of the block. //
// An out of sequence NAK may be due to a block //
// complement error. Go ahead and take it but wait //
// for a few seconds after the block has finished //
// transmitting to accept it as valid. //
//--------------------------------------------------//
if ((char)LongCh == NAK)
ExtraNAK = 1;
}
if (KBHIT && KBREAD == X_ESC) // monitor the keyboard
rval = ESC_PRESSED;
if (!port.Carrier()) // monitor the carrier
rval = NO_CARRIER;
}
TimeOuts = 0; //-------------------------------------------//
while (!rval) // transmit buffer empty now, block is sent //
{ //-------------------------------------------//
LongCh = RxWithTimeOut(18); // receive with 1 sec timeout
if (LongCh >= 0)
{
if ((char)LongCh == ACK) // if got an ACK, have a good
{ // block
FirstAck = 1;
BadBlock = 0;
BytesTransferred += (long)BlockSize;
vDisplay(3, 48, "%ld", BytesTransferred);
break;
}
if ((char)LongCh == NAK) // if got a NAK, have a bad
{ // block
BadBlock = 1;
break;
}
if ((char)LongCh == CAN && ++CANsRxd >= 2)
rval = GOT_CANNED; // remote cancelled transfer
else
CANsRxd = 0;
continue;
}
if (LongCh == TIMED_OUT)
{
if (++TimeOuts == 3 && ExtraNAK)
{ // If had gotten a NAK during the //
BadBlock = 1; // transmission of the block and //
break; // 3 secs have passed with no //
} // chars, assume NAK was genuine //
else if (TimeOuts >= 45)
rval = LongCh; // if nothing after 45 secs, abort
}
else if (LongCh < 0) // LongCh < 0 but not a timeout is
rval = LongCh; // is a fatal error
}
}
//////////////////////////////////////////////////////
// close file, free memory, reset params, & exit //
//////////////////////////////////////////////////////
fclose(fh);
delete buf;
if (rval == ESC_PRESSED)
{
Display(5, 48, "Aborting file transfer ...");
while (port.Carrier() && !port.TxEmpty())
;
SendCANs();
}
ResetXmodemMode(save_xoff, save_flushbad, save_params);
TransferWindow(0);
XmodemExitMsg(rval);
}
/////////////////////////////////////////////////
// XmodemExitMsg //
//://////////////////////////////////////////////
void XmodemExitMsg(int rval)
{
switch (rval) // display exit message
{
case 1:
Display("\nFile transferred\n");
break;
case 2:
Display("\nFile transferred, EOT unconfirmed\n");
break;
case TIMED_OUT:
Display("\nRemote fails to respond\n");
break;
case GOT_CANNED:
Display("\nRemote aborted transfer\n");
break;
case ERROR_LIMIT:
Display("\nMaximum error count exceeded\n");
break;
case ESC_PRESSED:
Display("File transfer aborted\n");
break;
case NO_CARRIER:
Display("\nCarrier lost\n");
break;
default:
Display("\nUnknown exit code\n");
break;
}
}
/////////////////////////////////////////////////
// RxWithTimeOut //
//://////////////////////////////////////////////
int RxWithTimeOut(int Ticks)
{
int LongCh;
if (!(B_RXEMPTY & (LongCh = port.Rx())))
return (LongCh & 0xff); // fast return if char available
Timer t(Ticks); // start timeout timer
while (1)
{
if (port.RxLevel())
return (port.Rx() & 0xff); // return char if available
if (!port.Carrier())
return NO_CARRIER; // return if carrier was lost
if (KBHIT && KBREAD == X_ESC)
return ESC_PRESSED; // return if ESC was pressed
if (t.Expired())
return TIMED_OUT; // return if Ticks have expierd
}
}
/////////////////////////////////////////////////
// SetXmodemMode //
//://////////////////////////////////////////////
void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
{
char new_params[10], *pc;
save_xoff = port.XFlow(); // make sure XON/XOFF is turned off
port.XFlow('\0');
strcpy(save_params, port.Params()); // make sure params are N,8,1
strcpy(new_params, save_params);
for (pc = new_params; isdigit(*pc); pc++)
; // skip over baudrate
strcpy(pc,"N81"); // new_params = baudrate+"N81"
port.Params(new_params);
DisplayParameters();
port.TxFlush(); // flush the transmit side only
save_flushbad = port.FlushBadChars();
port.FlushBadChars(1); // flush framing errors mode on
}
/////////////////////////////////////////////////
// ResetXmodemMode //
//://////////////////////////////////////////////
void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
{
port.XFlow(save_xoff); // reset to original XOFF mode
port.FlushBadChars(save_flushbad); // reset flush bad chars switch
port.Params(save_params); // back to original parameters
DisplayParameters();
}
/////////////////////////////////////////////////
// SendCANs //
//://////////////////////////////////////////////
void SendCANs()
{
port.Tx("\x18\x18\x18\x18\x18\b\b\b\b\b"); // send a bunch of control-X's
while (port.Carrier() && !port.TxEmpty())
;
port.RxFlush();
}
/////////////////////////////////////////////////
// WaitForBlockToEnd //
//://////////////////////////////////////////////
int WaitForBlockToEnd(int Ticks)
{
int LongCh;
while ((LongCh = RxWithTimeOut(Ticks)) >= 0)
; // wait for timeout or error
return (LongCh == TIMED_OUT) ? 0 : LongCh;
}
/////////////////////////////////////////////////
// GetHdrChar //
//://////////////////////////////////////////////
int GetHdrChar(int Ticks)
{
int LongCh, TimeOuts = 0, CANsRxd = 0;
while (1)
{
LongCh = RxWithTimeOut(1);
if ((char)LongCh == SOH || (char)LongCh == STX || (char)LongCh == EOT)
break; // got block header
if (LongCh == TIMED_OUT)
{
if (++TimeOuts > Ticks)
break; // exceeded max Ticks
}
else if (LongCh < 0)
break; // aborted, lost carrier, ...
if ((char)LongCh == CAN && ++CANsRxd >= 2)
{
LongCh = GOT_CANNED;
break; // remote cancelled
}
else
CANsRxd = 0; // garbage, reset CAN counter
}
return (LongCh);
}
/////////////////////////////////////////////////
// TransferWindow //
//://////////////////////////////////////////////
void TransferWindow(int Flag)
{
if (Flag == 0)
{
RemoveBox();
return;
}
DrawBox(0, 26, 7, 53);
v_color = ylw;
if (Flag > 0)
Display(1, 34, "Transmitting:");
else
Display(1, 37, "Receiving:");
Display(2, 35, "Total Bytes:");
Display(3, 29, "Bytes Transferred:");
Display(4, 28, "Consecutive Errors:");
Display(5, 36, "Last Error:");
v_color = wht;
}