www.pudn.com > zmodem.zip > ZMISC.C


/*--------------------------------------------------------------------------*/ 
/* FILE: zmisc.c    (Opus zmodem routines used by send and receive)         */ 
/*                                                                          */ 
/*                                                                          */ 
/*               The Opus Computer-Based Conversation System                */ 
/*       (c) Copyright 1986, Wynn Wagner III, All Rights Reserved           */ 
/*                                                                          */ 
/*      This implementation of Chuck Forsberg's ZMODEM protocol was         */ 
/*              for Opus by Rick Huebner and Wynn Wagner III                */ 
/*                                                                          */ 
/* (MSC/4 with /Zp /Ox)                                                     */ 
/*                                                                          */ 
/*                                                                          */ 
/*                                                                          */ 
/*                                                                          */ 
/*  This module is similar to a routine used by Opus-Cbcs (1.00).  It is    */ 
/*  provided for your information only.  You will find routines that need   */ 
/*  to be coded and identifiers to be resolved.                             */ 
/*                                                                          */ 
/*  There is absolutely no guarantee that anything here will work.  If you  */ 
/*  break this routine, you own both pieces.                                */ 
/*                                                                          */ 
/*  USAGE:  You may use this material in any program with no obligation     */ 
/*          as long as there is no charge for your program.  For more       */ 
/*          information about commercial use, contact the "OPUSinfo HERE"   */ 
/*          BBS (124/111).                                                  */ 
/*                                                                          */ 
/*  NOTE:   There are a couple of things the Opus implementation does that  */ 
/*          aren't part of the original ZModem protocol.  They all deal     */ 
/*          with WaZOO type ("ZedZap") netmail and should only show up when */ 
/*          used under that condition.                                      */ 
/*                                                                          */ 
/*             * The maximum packet size can grow larger than 1k.  It is    */ 
/*               sensitive to the baud rate.  (2400b=2048k; 9600b=8192k)    */ 
/*             * The sender must be able to send nothing.  In other words,  */ 
/*               the sending system must be able to initiate and terminate  */ 
/*               a zmodem send session without having to actually send a    */ 
/*               file.  Normally this kind of thing would never happen in   */ 
/*               zmodem.                                                    */ 
/*                                                                          */ 
/*                                                                          */ 
/*--------------------------------------------------------------------------*/ 
#include "zmodem.h" 
 
 
 
static int Rxtype;               /* Type of header received                 */ 
static int Rxframeind;           /* ZBIN ZBIN32,ZHEX type of frame received */ 
 
static char hex[] = "0123456789abcdef"; 
 
/* Send a byte as two hex digits */ 
#define Z_PUTHEX(i,c) {i=(c);SENDBYTE(hex[((i)&0xF0)>>4]);SENDBYTE(hex[(i)&0xF]);} 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Routines used by other ZModem modules...                                 */ 
/*--------------------------------------------------------------------------*/ 
int  pascal Z_GetByte(int); 
void pascal Z_PutString(unsigned char *); 
void pascal Z_SendHexHeader(unsigned short,unsigned char *); 
int  pascal Z_GetHeader(unsigned char *); 
int  pascal Z_GetZDL(void); 
void pascal Z_PutLongIntoHeader(long); 
unsigned short pascal Z_UpdateCRC(unsigned short,unsigned short); 
 
 
/*--------------------------------------------------------------------------*/ 
/* Private routines                                                         */ 
/*--------------------------------------------------------------------------*/ 
static int  pascal _Z_qk_read(void); 
static int  pascal _Z_GetBinaryHeader(unsigned char *); 
static int  pascal _Z_GetHexHeader(unsigned char *); 
static int  pascal _Z_GetHex(void); 
static int  pascal _Z_TimedRead(void); 
static long pascal _Z_PullLongFromHeader(unsigned char *); 
 
 
 
extern byte *local_CEOL; 
extern char *KBD_msg; 
 
 
void pascal z_message( s ) 
   byte *s; 
   begin 
   	gotoxy( locate_x+20, locate_y ); 
      if (s) cputs(s); 
      cputs( local_CEOL ); 
   end 
 
 
void z_log(s) 
   byte *s; 
   begin 
      word x, y; 
 
      z_message(s); 
 
      x  = locate_x; 
      y  = locate_y; 
      status_line(s); /* also does disk file logging */ 
      locate_x = x; 
      locate_y = y; 
 
   end 
 
 
void show_loc(l,w) 
   unsigned long  l; 
   unsigned int   w; 
   begin 
      gotoxy( locate_x+35, locate_y ); 
      cprintf("Ofs=%ld Retries=%d%s",l,w,local_CEOL); 
   end 
 
 
byte * pascal zalloc() 
   begin 
      byte *sptr; 
 
      sptr  = malloc(WAZOOMAX); 
      if (!sptr) 
         begin 
         	status_line("!Z-MEMOVFL"); 
            adios(2); 
         end 
      return sptr; 
   end 
 
 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET BYTE                                                               */ 
/* Get a byte from the modem;                                               */ 
/* return TIMEOUT if no read within timeout tenths,                         */ 
/* return RCDO if carrier lost                                              */ 
/*--------------------------------------------------------------------------*/ 
int pascal Z_GetByte(tenths) 
   register int tenths; 
   begin 
      register int i; 
 
      i^=i; 
      do 
      	begin 
            if (CHAR_AVAIL()) return MODEM_IN(); 
         end 
      while((i++)<2000); 
 
 
      i   = tenths/10; 
      if (i<1)   i =  1; 
      if (i>58)  i = 58; 
 
      i = set_timer(i); 
 
      while(!CHAR_AVAIL()) 
         begin 
            if (!CARRIER)     return RCDO; 
 
            time_release(); 
            if (time_gone(i)) return TIMEOUT; 
         end 
 
      return MODEM_IN(); 
 
   end 
 
 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* QK_READ  (like Z_GetByte, but assumes the time to be Rxtimeout)          */ 
/* Get a byte from the modem;                                               */ 
/* return TIMEOUT if no read within timeout tenths,                         */ 
/* return RCDO if carrier lost                                              */ 
/*--------------------------------------------------------------------------*/ 
static int pascal _Z_qk_read() 
   begin 
      register int i; 
      register int time_val; 
 
      time_val^=time_val; 
      do 
      	begin 
            if ((i=MODEM_STATUS())&DATA_READY)  return MODEM_IN(); 
            if (!(i&ctl.carrier_mask))          return RCDO; 
         end 
      while((time_val++)<2000); 
 
      time_val = set_timer(Rxtimeout); 
      do 
         begin 
            if ((i=MODEM_STATUS())&DATA_READY)  return MODEM_IN(); 
            if (!(i&ctl.carrier_mask))          return RCDO; 
            time_release(); 
         end 
      while(time_left(time_val)); 
 
      return TIMEOUT; 
 
   end 
 
 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z PUT STRING                                                             */ 
/* Send a string to the modem, processing for \336 (sleep 1 sec)            */ 
/* and \335 (break signal, ignored)                                         */ 
/*--------------------------------------------------------------------------*/ 
void pascal Z_PutString(s) 
   register unsigned char *s; 
   begin 
      register int c; 
 
      while (*s) 
         begin 
            switch (c = *s++) 
               begin 
                  case '\336':   big_pause(2); 
                  case '\335':   break; 
                  default:       SENDBYTE(c); 
               end /* switch */ 
 
         end /* while */ 
 
   end /* Z_PutString */ 
 
 
 
 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z UPDATE CRC                                                             */ 
/* update CRC                                                               */ 
/*--------------------------------------------------------------------------*/ 
unsigned short pascal Z_UpdateCRC(c, crc) 
   unsigned short c; 
   unsigned short crc; 
   begin 
      register int    count; 
      register word   temp; 
 
      temp   = crc; 
 
      for (count=8; --count>=0; ) 
         begin 
            if (temp & 0x8000) 
               begin 
                  temp <<= 1; 
                  temp += (((c<<=1) & 0400)  !=  0); 
                  temp ^= 0x1021; 
               end 
            else 
               begin 
                  temp <<= 1; 
                  temp += (((c<<=1) & 0400)  !=  0); 
               end 
         end /* for */ 
 
      return temp; 
 
   end /* z_crc update */ 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z SEND HEX HEADER                                                        */ 
/* Send ZMODEM HEX header hdr of type type                                  */ 
/*--------------------------------------------------------------------------*/ 
void pascal Z_SendHexHeader(type, hdr) 
   unsigned short type; 
   unsigned char *hdr; 
   begin 
      register int   n; 
      register int   i; 
      unsigned short crc; 
 
      SENDBYTE(ZPAD); 
      SENDBYTE(ZPAD); 
      SENDBYTE(ZDLE); 
      SENDBYTE(ZHEX); 
      Z_PUTHEX(i,type); 
 
      crc = Z_UpdateCRC(type, 0); 
      for (n=4; --n >= 0;) 
         begin 
            Z_PUTHEX(i,(*hdr)); 
            crc = Z_UpdateCRC(((unsigned short)(*hdr++)), crc); 
         end 
      crc = Z_UpdateCRC(0,crc); 
      crc = Z_UpdateCRC(0,crc); 
      Z_PUTHEX(i,(crc>>8)); 
      Z_PUTHEX(i,crc); 
 
      /* Make it printable on remote machine */ 
      SENDBYTE('\r'); 
      SENDBYTE('\n'); 
 
      /* Uncork the remote in case a fake XOFF has stopped data flow */ 
      if (type != ZFIN) SENDBYTE(021); 
 
      wait_for_clear(); 
 
   end /* Z_SendHexHeader */ 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET HEADER                                                             */ 
/* Read a ZMODEM header to hdr, either binary or hex.                       */ 
/*   On success, set Zmodem to 1 and return type of header.                 */ 
/*   Otherwise return negative on error                                     */ 
/*--------------------------------------------------------------------------*/ 
int pascal Z_GetHeader(hdr) 
   byte *hdr; 
   begin 
 
      register int   c; 
      register int   n; 
      int            cancount; 
 
 
      n        = cur_baud;   /* Max characters before start of frame */ 
      cancount = 5; 
 
Again: 
 
      if (((KEYPRESS()) and (READKB()==27))) 
         begin 
            send_can(); 
            z_log( KBD_msg ); 
            return ZCAN; 
         end 
 
      Rxframeind = Rxtype = 0; 
 
      switch (c = _Z_TimedRead()) 
         begin 
 
            case ZPAD:     /*-----------------------------------------------*/ 
                           /* This is what we want.                         */ 
                           /*-----------------------------------------------*/ 
                           break; 
 
            case RCDO: 
            case TIMEOUT:  /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Done; 
 
            case CAN:      /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           if (--cancount <= 0) 
                              begin 
                                 c = ZCAN; 
                                 goto Done; 
                              end 
 
                           /* fallthrough... */ 
 
            default:       /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
Agn2: 
 
                           if (--n <= 0) 
                              begin 
                                 z_log( FUBAR_msg ); 
                                 send_can(); 
                                 return ZCAN; 
                              end 
 
                           if (c != CAN) cancount = 5; 
                           goto Again; 
 
         end /* switch */ 
 
      cancount = 5; 
 
Splat: 
 
      switch (c = _Z_TimedRead()) 
         begin 
            case ZDLE:     /*-----------------------------------------------*/ 
                           /* This is what we want.                         */ 
                           /*-----------------------------------------------*/ 
                           break; 
 
            case ZPAD:     /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Splat; 
 
            case RCDO: 
            case TIMEOUT:  /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Done; 
 
            default:       /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Agn2; 
 
         end /* switch */ 
 
 
      switch (c = _Z_TimedRead()) 
         begin 
 
            case ZBIN:     /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           Rxframeind = ZBIN; 
                           c =  _Z_GetBinaryHeader(hdr); 
                           break; 
 
            case ZHEX:     /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           Rxframeind = ZHEX; 
                           c =  _Z_GetHexHeader(hdr); 
                           break; 
 
            case CAN:      /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           if (--cancount <= 0) 
                              begin 
                                 c = ZCAN; 
                                 goto Done; 
                              end 
                           goto Agn2; 
 
            case RCDO: 
            case TIMEOUT:  /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Done; 
 
            default:       /*-----------------------------------------------*/ 
                           /*                                               */ 
                           /*-----------------------------------------------*/ 
                           goto Agn2; 
 
         end /* switch */ 
 
      Rxpos = _Z_PullLongFromHeader(hdr); 
 
Done: 
 
      return c; 
 
   end /* Z_GetHeader */ 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET BINARY HEADER                                                      */ 
/* Receive a binary style header (type and position)                        */ 
/*--------------------------------------------------------------------------*/ 
static int pascal _Z_GetBinaryHeader(hdr) 
   byte *hdr; 
   begin 
      register int    c; 
      register word   crc; 
      int             n; 
 
      if ((c   = Z_GetZDL()) & ~0xFF)   return c; 
      Rxtype   = c; 
      crc      = Z_UpdateCRC(c, 0); 
 
      for (n=4; --n >= 0;) 
         begin 
            if ((c = Z_GetZDL()) & ~0xFF) return c; 
            crc = Z_UpdateCRC(c, crc); 
            *hdr++ = c; 
         end 
      if ((c   = Z_GetZDL()) & ~0xFF) return c; 
 
      crc      = Z_UpdateCRC(c, crc); 
      if ((c   = Z_GetZDL()) & ~0xFF) return c; 
 
      crc = Z_UpdateCRC(c, crc); 
      if (crc & 0xFFFF) 
         begin 
            z_message( CRC_msg ); 
            return ERROR; 
         end 
 
      return Rxtype; 
 
   end /* _Z_GetBinaryHeader */ 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET HEX HEADER                                                         */ 
/* Receive a hex style header (type and position)                           */ 
/*--------------------------------------------------------------------------*/ 
static int pascal _Z_GetHexHeader(hdr) 
   unsigned char *hdr; 
   begin 
      register int   c; 
      register word  crc; 
      int            n; 
 
      if ((c   = _Z_GetHex()) < 0) return c; 
      Rxtype   = c; 
      crc      = Z_UpdateCRC(c, 0); 
 
      for (n=4; --n >= 0;) 
         begin 
            if ((c = _Z_GetHex()) < 0) return c; 
            crc      = Z_UpdateCRC(c, crc); 
            *hdr++   = c; 
         end 
 
      if ((c = _Z_GetHex()) < 0) return c; 
      crc = Z_UpdateCRC(c, crc); 
      if ((c = _Z_GetHex()) < 0) return c; 
      crc = Z_UpdateCRC(c, crc); 
      if (crc & 0xFFFF) 
         begin 
            z_message( CRC_msg ); 
            return ERROR; 
         end 
      if (Z_GetByte(1) == '\r') Z_GetByte(1);  /* Throw away possible cr/lf */ 
 
      return Rxtype; 
 
   end 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET HEX                                                                */ 
/* Decode two lower case hex digits into an 8 bit byte value                */ 
/*--------------------------------------------------------------------------*/ 
static int pascal _Z_GetHex() 
   begin 
      register int c, n; 
 
      if ((n = _Z_TimedRead()) < 0) return n; 
      n -= '0'; 
      if (n > 9) n -= ('a' - ':'); 
      if (n & ~0xF) return ERROR; 
 
      if ((c = _Z_TimedRead()) < 0) return c; 
      c -= '0'; 
      if (c > 9) c -= ('a' - ':'); 
      if (c & ~0xF) return ERROR; 
 
      return (n<<4 | c); 
   end 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z GET ZDL                                                                */ 
/* Read a byte, checking for ZMODEM escape encoding                         */ 
/* including CAN*5 which represents a quick abort                           */ 
/*--------------------------------------------------------------------------*/ 
int pascal Z_GetZDL() 
   begin 
      register int c; 
 
      if ((c = _Z_qk_read()) != ZDLE)         return c; 
 
      switch (c=_Z_qk_read()) 
         begin 
            case CAN:   return ((c=_Z_qk_read())<0)?               c : 
                               ((c==CAN) && ((c=_Z_qk_read())<0))? c : 
                               ((c==CAN) && ((c=_Z_qk_read())<0))? c : (GOTCAN); 
 
            case ZCRCE: 
            case ZCRCG: 
            case ZCRCQ: 
            case ZCRCW: return (c | GOTOR); 
 
            case ZRUB0: return 0x7F; 
 
            case ZRUB1: return 0xFF; 
 
            default:    return   (c<0)?            c : 
                                 ((c&0x60)==0x40)? (c ^ 0x40)  : ERROR; 
 
         end /* switch */ 
 
   end /* Z_GetZDL */ 
 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z TIMED READ                                                             */ 
/* Read a character from the modem line with timeout.                       */ 
/*  Eat parity, XON and XOFF characters.                                    */ 
/*--------------------------------------------------------------------------*/ 
static int pascal _Z_TimedRead() 
   begin 
      register int c; 
 
      for (;;) 
         begin 
            if ((c = _Z_qk_read()) < 0) return c; 
 
            switch (c &= 0x7F) 
               begin 
                  case XON: 
                  case XOFF:  continue; 
 
                  default:    return c; 
               end /* switch */ 
 
         end /* for */ 
 
   end /* _Z_TimedRead */ 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z LONG TO HEADER                                                         */ 
/* Store long integer pos in Txhdr                                          */ 
/*--------------------------------------------------------------------------*/ 
void pascal Z_PutLongIntoHeader(pos) 
   long pos; 
   begin 
      Txhdr[ZP0] = pos; 
      Txhdr[ZP1] = pos>>8; 
      Txhdr[ZP2] = pos>>16; 
      Txhdr[ZP3] = pos>>24; 
   end /* Z_PutLongIntoHeader */ 
 
 
 
 
/*--------------------------------------------------------------------------*/ 
/* Z PULL LONG FROM HEADER                                                  */ 
/* Recover a long integer from a header                                     */ 
/*--------------------------------------------------------------------------*/ 
static long pascal _Z_PullLongFromHeader(hdr) 
   unsigned char *hdr; 
   begin 
      long l; 
 
      l = hdr[ZP3]; 
      l = (l << 8) | hdr[ZP2]; 
      l = (l << 8) | hdr[ZP1]; 
      l = (l << 8) | hdr[ZP0]; 
      return l; 
   end /* _Z_PullLongFromHeader */ 
 
 
/* END OF FILE: n_zmodem.c */