home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol150 / 211modem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1984-04-29  |  24.5 KB  |  869 lines

  1. /************************************************************************/
  2. /*                modem.c                 */
  3. /*                                    */
  4. /*        modem code for Citadel bulletin board system        */
  5. /*    NB: this code is rather machine-dependent:  it will typically    */
  6. /*    need some twiddling for each new installation.            */
  7. /*                  82Nov05 CrT                */
  8. /************************************************************************/
  9.  
  10. /************************************************************************/
  11. /*                history                 */
  12. /*                                    */
  13. /* 83Mar01 CrT    FastIn() ignores LFs etc -- CRLF folks won't be trapped.*/
  14. /* 83Feb25 CrT    Possible fix for backspace-in-message-entry problem.    */
  15. /* 83Feb18 CrT    fastIn() upload mode cutting in on people.  Fixed.    */
  16. /* 82Dec16 dvm    modemInit revised for FDC-1, with kludge for use with    */
  17. /*        Big Board development system                */
  18. /* 82Dec06 CrT    2.00 release.                        */
  19. /* 82Nov15 CrT    readfile() & sendfile() borrowed from TelEdit.c     */
  20. /* 82Nov05 CrT    Individual history file established            */
  21. /************************************************************************/
  22.  
  23. #include "b:210ctdl.h"
  24.  
  25. /************************************************************************/
  26. /*                Contents                */
  27. /*                                    */
  28. /*    BBSCharReady()        returns true if user input is ready    */
  29. /*  #    fastIn()        kludge code compiling other stuff inline*/
  30. /*    getCh()         bottom-level console-input filter    */
  31. /*  #    getMod()        bottom-level modem-input   filter    */
  32. /*    interpret()        interprets a configuration routine    */
  33. /*    KBReady()        returns TRUE if a console char is ready */
  34. /*    getCh()         returns a console char            */
  35. /*    iChar()         top-level user-input function        */
  36. /*    interact()        chat mode                */
  37. /*    modIn()         returns a user char            */
  38. /*    modemInit()        top-level initialize-all-modem-stuff    */
  39. /*    mOReady()        returns true if modem can accept a char */
  40. /*    oChar()         top-level user-output function        */
  41. /*  #    outMod()        bottom-level modem output        */
  42. /*    pause()         pauses for N/100 seconds        */
  43. /*    putChar()                            */
  44. /*    recieve()        read modem char or time out        */
  45. /*    readFile()        accept a file using WC protocol     */
  46. /*    ringSysop()        signal chat-mode request        */
  47. /*    sendWCChar()        send file with WC-protocol handshaking    */
  48. /*                                    */
  49. /*    # == routines you should certainly check when porting system    */
  50. /************************************************************************/
  51.  
  52. /************************************************************************/
  53. /*        The principal dependencies:                */
  54. /*                                    */
  55. /*  iChar   modIn                    outMod        */
  56. /*        modIn   getMod  getCh   mIReady kBReady outMod  carrDetect    */
  57. /*            getMod                        */
  58. /*                getCh                    */
  59. /*                    mIReady                */
  60. /*                        kBReady            */
  61. /*                                carrDetect    */
  62. /*                                    */
  63. /*  oChar                        outMod        */
  64. /*                            outMod  mOReady    */
  65. /************************************************************************/
  66.  
  67.  
  68. /************************************************************************/
  69. /*    BBSCharReady() returns TRUE if char is available from user    */
  70. /*    NB: user may be on modem, or may be sysop in CONSOLE mode    */
  71. /************************************************************************/
  72. char BBSCharReady()
  73. {
  74.     char KBReady();
  75.  
  76.     return ((haveCarrier       &&   interpret(pMIReady))
  77.      || (whichIO==CONSOLE  &&   KBReady()           )
  78.     );
  79. }
  80.  
  81. /************************************************************************/
  82. /*    fastIn() is  a special kludge to read in text from the modem    */
  83. /*    as quickly as possible, to allow message upload without     */
  84. /*    handshaking.  Hence we hand-compile some other routines inline    */
  85. /*    Called only from getText(), when whichIO==MODEM.        */
  86. /*    Externals:    see below                     */
  87. /*                                    */
  88. /*    This code is probably overkill for 300 baud, but it may handle    */
  89. /*    1200 baud as well.                        */
  90. /*                                    */
  91. /*    code being speed-optimized would normally be:            */
  92. /*                                    */
  93. /*    while (                             */
  94. /*        !(    (c=iChar()) == NEWLINE     &&   buf[i-1] == NEWLINE )    */
  95. /*        &&    i < lim                         */
  96. /*        &&    (haveCarrier || whichIO == CONSOLE)            */
  97. /*    ) {                                */
  98. /*        if (c != BACKSPACE)  buf[i++] = c;                */
  99. /*        else {                            */
  100. /*        /  handle deletes:     /                */
  101. /*        oChar(' ');                        */
  102. /*        oChar(BACKSPACE);                    */
  103. /*        if (i>0  &&  buf[i-1] != NEWLINE)  i--;         */
  104. /*        else                   oChar(BELL);     */
  105. /*        }                                */
  106. /*    }                                */
  107. /************************************************************************/
  108. #define cursor        fpc1        /* external char *        */
  109. #define BUFEND        fpc2        /* external char *        */
  110. #define tryCount    fi1        /* external int         */
  111. #define isFast        fi2        /* external int         */
  112. #define ch        fc1        /* external char        */
  113. #define lastWasNL    fc2        /* external char        */
  114. #define slow        fc3        /* external char        */
  115. fastIn(continuing)
  116. char continuing;    /* TRUE if we are continuing a message    */
  117. {
  118.     char cache, notFinished;
  119.     int  tryStart, shortTime;
  120.  
  121.     isFast    = 0;
  122.  
  123.     tryStart    = 500*megaHz;
  124.     shortTime    = 480*megaHz;
  125.     cursor    = &msgBuf.mbtext[0      ];
  126.     BUFEND    = &msgBuf.mbtext[MAXTEXT-1];
  127.  
  128.     if (continuing)   while (*cursor) ++cursor; /* find where we were    */
  129.  
  130.     notFinished = TRUE;
  131.     lastWasNL    = FALSE;
  132.  
  133.     /* put newline at start of buffer to simplify BACKSPACE check:    */
  134.     cache          = msgBuf.mbtext[-1];
  135.     msgBuf.mbtext[-1]      = NEWLINE;
  136.  
  137.     while (notFinished     &&   cursor < BUFEND) {
  138.     tryCount = tryStart;    /* try to be waiting for each char    */
  139.     while (--tryCount  &&  !interpret(pMIReady));
  140.  
  141.     if (tryCount>shortTime) isFast++;
  142.     else            isFast--;
  143.  
  144.     if (!tryCount) {
  145.         /* no modem char -- take break to check other stuff */
  146.         if (KBReady()) {
  147.         if (getCh() == SPECIAL) {
  148.             mprintf("\n system is now in CONSOLE mode.\n ");
  149.             whichIO    = CONSOLE;
  150.             notFinished = FALSE;
  151.             setUp(FALSE);
  152.         }
  153.         }
  154.         if (!interpret(pCarrDetect)) {
  155.         modIn();    /* let modIn() announce it etc    */
  156.         notFinished    = FALSE;
  157.         }
  158.     } else {
  159.         /* time to read modem char: */
  160.         switch (ch =  filter[ inp(mData) & 0x7F ]) {
  161.         case NEWLINE:
  162.         if (lastWasNL)    {
  163.             notFinished = FALSE;
  164.         } else {
  165.             lastWasNL    = TRUE;
  166.  
  167.             *cursor++    = ch;
  168.  
  169.             if (isFast>0  ||  !termLF)     ch = '\r';
  170.             else {
  171.             oChar('\r');
  172.             while (!interpret(pMOReady));    /* for outp()    */
  173.             }
  174.         }
  175.         isFast    = 0;    /* figure speed of each line independently */
  176.         break;
  177.         case BACKSPACE:
  178.         /* people don't upload backspaces -- one hopes! --    */
  179.         /* so we don't worry about speed so much here:        */
  180.         if (*--cursor == NEWLINE) {
  181.             /* trying to erase to previous line -- disallow    */
  182.             /* because we have no upline:            */
  183.             ch = BELL;
  184.             cursor++;
  185.         } else {
  186.             oChar(BACKSPACE);
  187.             oChar(' ');     /* erase last char            */
  188.         }
  189.         while (!interpret(pMOReady));        /* for outp()    */
  190.         break;
  191.         case '\0':
  192.         /* ignore unwanted chars completely: */
  193.         break;
  194.         default:
  195.         lastWasNL    = FALSE;
  196.         *cursor++    = ch;
  197.         break;
  198.         }
  199.         /* echo to console--expendable but nice: */
  200.         if (thisRoom!=1 || whichIO==CONSOLE)   putCh(ch);
  201.         while (!interpret(pMOReady));        /* for outp()   */
  202.         outp(mData, ch);    /* (was:)assume port is ready by now          */
  203.     }
  204.     }
  205.     *cursor        = '\0';     /* tie off message        */
  206.  
  207.     if (cursor == BUFEND) {
  208.     mprintf("\n \7BUFFER OVERFLOW\n ");        return;
  209.     }
  210.  
  211.     msgBuf.mbtext[-1]    = cache;    /* return borrowed space    */
  212. }
  213.  
  214.  
  215. /************************************************************************/
  216. /*    getCh() reads a console char                    */
  217. /*        In CONSOLE mode, CRs are changed to newlines        */
  218. /*        Rubouts are changed to backspaces                */
  219. /*    Returns:    resulting char                    */
  220. /************************************************************************/
  221. char getCh()
  222. {
  223.     char c;
  224.     char bios();
  225.  
  226.     return bios(3);
  227. }
  228.  
  229. /************************************************************************/
  230. /*    getMod() is bottom-level modem-input routine            */
  231. /*      kills any parity bit                        */
  232. /*      rubout            -> backspace            */
  233. /*      CR                -> newline            */
  234. /*      other nonprinting chars    -> blank            */
  235. /*    Returns: result                         */
  236. /************************************************************************/
  237. char getMod() {
  238.     char inp();
  239.  
  240.     return inp(mData) & 0x7F;
  241. }
  242.  
  243. /************************************************************************/
  244. /*    iChar() is the top-level user-input function -- this is the    */
  245. /*    function the rest of Citadel uses to obtain user input        */
  246. /************************************************************************/
  247. char iChar() {
  248.     char modIn();
  249.     char c;
  250.  
  251.     if (justLostCarrier)   return 0;    /* ugly patch    */
  252.  
  253.     c = filter[modIn()];
  254.  
  255.     switch (echo) {
  256.     case BOTH:
  257.     if (haveCarrier) {
  258.         if (c == '\n')    doCR();
  259.         else        outMod(c);
  260.     }
  261.     if (thisRoom!=1 || whichIO==CONSOLE)  putCh(c);
  262.     break;
  263.     case CALLER:
  264.     if (whichIO == MODEM) {
  265.         if (c == '\n')    doCR();
  266.         else        outMod(c);
  267.     } else {
  268.         putCh(c);
  269.     }
  270.     break;
  271.     }
  272.     return(c);
  273. }
  274.  
  275. /************************************************************************/
  276. /*    interact() allows the sysop to interact directly with        */
  277. /*    whatever is on the modem.                   dvm 9-82 */
  278. /************************************************************************/
  279. interact() {
  280.     char c, lineEcho, lineFeeds, localEcho;
  281.     char getMod(), getCh(), interpret();
  282.  
  283.     printf(" Direct modem-interaction mode\n");
  284.     lineEcho    = getYesNo("Echo to modem"     );
  285.     localEcho    = getYesNo("Echo keyboard"     );
  286.     lineFeeds    = getYesNo("Echo CR as CRLF"   );
  287.     printf("<ESC> to exit\n");
  288.  
  289.     /* incredibly ugly code.  Rethink sometime: */
  290.     while (c!=SPECIAL) {
  291.     c = 0;
  292.  
  293.     if (c=interpret(pMIReady) ) {
  294.         c  = inp(mData) & 0x7F;
  295.         if (c != '\r') c = filter[c];
  296.         if (c != '\r') {
  297.         if (lineEcho)    outMod(c);
  298.         putCh(c);
  299.         } else {
  300.         if (!lineFeeds) {
  301.             if (lineEcho) outMod('\r');
  302.             putCh('\r');
  303.         } else {
  304.             if (lineEcho) {
  305.             outMod('\r');
  306.             outMod('\n');
  307.             }
  308.             putCh('\r');
  309.             putCh('\n');
  310.         }
  311.         }
  312.     }
  313.     if (KBReady()) {
  314.         c  = filter[getCh()];
  315.         if (c != NEWLINE) {
  316.         if (localEcho)    putCh(c);
  317.         outMod(c);
  318.         } else {
  319.         if (!lineFeeds) {
  320.             if (localEcho) putCh('\r');
  321.             outMod('\r');
  322.         } else {
  323.             if (lineEcho) {
  324.             putCh('\r');
  325.             putCh('\n');
  326.             }
  327.             outMod('\r');
  328.             outMod('\n');
  329.         }
  330.         }
  331.     }
  332.     }
  333. }
  334.  
  335. /************************************************************************/
  336. /*    interpret() interprets a configuration routine            */
  337. /*    Returns byte value computed                    */
  338. /************************************************************************/
  339. char interpret(instr)
  340. union {
  341.     char **pp;
  342.     int  *pi;
  343.     char *pc;
  344. } instr;
  345. {
  346.     char inp();
  347.     char accum;     /* our sole accumulator */
  348.     char *prompt;
  349.     int  lowLim, topLim;
  350.  
  351.     while (TRUE) {
  352.     switch (*instr.pc++) {
  353.     case RET:    return accum;                break;
  354.     case ANDI:    accum           &= *instr.pc++;        break;
  355.     case INP:    accum        = inp(*instr.pc++);    break;
  356.     case XORI:    accum           ^= *instr.pc++;        break;
  357.  
  358.     case LOAD:    accum        = *(*instr.pp++);    break;
  359.     case LOADI:    accum        = *instr.pc++;        break;
  360.     case LOADX:    accum        = scratch[*instr.pc++]; break;
  361.     case ORI:    accum           |= *instr.pc++;        break;
  362.     case OUTP:    outp(*instr.pc++, accum);        break;
  363.     case PAUSEI:    pause(*instr.pc++);            break;
  364.     case STORE:    *(*instr.pp++)    = accum;        break;
  365.     case STOREX:    scratch[*instr.pc++]    = accum;    break;
  366.     case OPRNUMBER:
  367.         prompt    = instr.pc;
  368.         while(*instr.pc++);     /* step over prompt    */
  369.         lowLim    = *instr.pc++;
  370.         topLim    = *instr.pc++;
  371.         accum    = getNumber(prompt, lowLim, topLim);
  372.         break;
  373.     case OUTSTRING:
  374.         while(*instr.pc) {
  375.         pause(5);    /* SmartModem can't handle 300 baud    */
  376.         outMod(*instr.pc++);    /* output string */
  377.         }
  378.         instr.pc++;                 /* skip null     */
  379.         break;
  380.     default:
  381.         printf("intrp-no opcod%d", *(instr.pc-1));
  382.         break;
  383.     }
  384.     }
  385. }
  386.  
  387. /************************************************************************/
  388. /*    KBReady() returns TRUE if a console char is ready        */
  389. /************************************************************************/
  390. char KBReady()
  391. {
  392.     char bios();
  393.  
  394.     return bios(2);
  395. }
  396.  
  397. /************************************************************************/
  398. /*    modemInit() is responsible for all modem-related initialization */
  399. /*    at system startup                        */
  400. /*    Globals modified:    haveCarrier    visibleMode        */
  401. /*                whichIO     modStat         */
  402. /*                exitToCpm    justLostCarrier     */
  403. /*    modified 82Dec10 to set FDC-1 SIO-B clock speed at        */
  404. /*    300 baud     -dvm                        */
  405. /************************************************************************/
  406. modemInit() {
  407.     char c;
  408.  
  409.     newCarrier        = FALSE;
  410.     visibleMode     = FALSE;
  411.     exitToCpm        = FALSE;
  412.     justLostCarrier    = FALSE;
  413.  
  414.     whichIO        = CONSOLE;
  415.  
  416. #ifdef FDC-1
  417. #define MONBASE 0xF800
  418. #define STSSP    MONBASE+0x045
  419.     call(STSSP, 0, 0, 0x0B, 0x05);    /* 300 baud on SIO-B */
  420. #endif
  421.  
  422. #ifdef VFC-2
  423.     /* dummy call (to CONSTAT) to make code size equal for both systems */
  424.     call(0xF006, 0, 0, 0x0B, 0x05);
  425. #endif
  426.  
  427.     if (!rcpm) {
  428.     interpret(pInitPort);
  429.     interpret(pHangUp);
  430.     }
  431.     haveCarrier = modStat = interpret(pCarrDetect);
  432. }
  433.  
  434. /************************************************************************/
  435. /* modIn() toplevel modem-input function                */
  436. /*   If DCD status has changed since the last access, reports        */
  437. /*   carrier present or absent and sets flags as appropriate.        */
  438. /*   In case of a carrier loss, waits 20 ticks and rechecks        */
  439. /*   carrier to make sure it was not a temporary glitch.        */
  440. /*   If carrier is newly received, returns newCarrier = TRUE;  if    */
  441. /*   carrier lost returns 0.  If carrier is present and state        */
  442. /*   has not changed, gets a character if present and            */
  443. /*   returns it.  If a character is typed at the console,        */
  444. /*   checks to see if it is keyboard interrupt character.  If        */
  445. /*   so, prints short-form console menu and awaits next         */
  446. /*   keyboard character.                        */
  447. /* Globals modified:    carrierDetect    modStat     haveCarrier    */
  448. /*            justLostCarrier whichIO     exitToCpm    */
  449. /*            visibleMode                    */
  450. /* Returns:    modem or console input character,            */
  451. /*        or above special values                 */
  452. /************************************************************************/
  453. char modIn() {
  454.     char    getMod(), getCh(), interpret(), KBReady();
  455.     char    c;
  456.     unsigned    hi, lo;
  457.  
  458.     hi    = (HITIMEOUT * megaHz);
  459.     lo    = 0xFF;
  460.     while (TRUE) {
  461.     if ((whichIO==MODEM) && (c=interpret(pCarrDetect)) != modStat) {
  462.         /* carrier changed     */
  463.         if (c)  {       /* carrier present    */
  464.         printf("Carr-detect\n");
  465.         haveCarrier = TRUE;
  466.         pause(200);
  467.         modStat     = c;
  468.         newCarrier  = TRUE;
  469.         return(0);
  470.         } else {
  471.         pause(200);            /* confirm it's not a glitch */
  472.         if (!interpret(pCarrDetect)) {      /* check again */
  473.             printf("Carr-loss\n");
  474.  
  475.             haveCarrier     = FALSE;
  476.             modStat        = FALSE;
  477.             justLostCarrier = TRUE;
  478.  
  479. /*            while(interpret(pMIReady)) getMod();    eat garbage */
  480.  
  481.             return(0);
  482.         }
  483.         }
  484.     }
  485.  
  486.     if (interpret(pMIReady)) {
  487.         if (haveCarrier) {
  488.         c = getMod();
  489.         if (whichIO == MODEM)    return c;
  490.         }
  491.     }
  492.  
  493.     if (KBReady()) {
  494.         c = getCh();
  495.         if (whichIO == CONSOLE) return(c);
  496.         else {
  497.         if (c == SPECIAL) {
  498.             printf("CONSOLE mode\n ");
  499.             whichIO = CONSOLE;
  500.             setUp(FALSE);
  501.             return 0;
  502.         }
  503.         }
  504.     }
  505.  
  506.     /* check for no input.    (Short-circuit evaluation, remember!) */
  507.     if (whichIO==MODEM  &&    haveCarrier  &&  !--lo    &&  !--hi) {
  508.         mprintf("Sleeping? Call again :-)");
  509.         interpret(pHangUp);
  510.     }
  511.     }
  512. }
  513.  
  514. /************************************************************************/
  515. /*    oChar() is the top-level user-output function            */
  516. /*      sends to modem port and console both                */
  517. /*      does conversion to upper-case etc as necessary        */
  518. /*      in "debug" mode, converts control chars to uppercase letters    */
  519. /*    Globals modified:    prevChar                */
  520. /************************************************************************/
  521. oChar(c)
  522. char c;
  523. {
  524.     prevChar = c;            /* for end-of-paragraph code    */
  525.     if (outFlag) return;        /* s(kip) mode            */
  526.  
  527.     if (termUpper)    c = toupper(c);
  528.     if (debug)        c = visible(c);
  529.     if (c == NEWLINE)    c = ' ';    /* doCR() handles real newlines */
  530.  
  531.     /* show on console            */
  532.     if (whichIO==CONSOLE  || (thisRoom!=1 && echo!=CALLER))   putCh(c);
  533.  
  534.     if (haveCarrier)  {
  535.     if (!usingWCprotocol) {
  536.         outMod(c);            /* show on modem        */
  537.     } else {
  538.         sendWCChar(c);
  539.     }
  540.     } else {
  541.     if (usingWCprotocol) {
  542.         sendWCChar(c);
  543.     }
  544.     }
  545. }
  546.  
  547.  
  548. /************************************************************************/
  549. /*    outMod stuffs a char out the modem port             */
  550. /************************************************************************/
  551. outMod(c)
  552. char c;
  553. {
  554.     while(!interpret(pMOReady));
  555.     outp(mData, c);
  556. }
  557.  
  558. /************************************************************************/
  559. /*    pause() busy-waits N/100 seconds                */
  560. /************************************************************************/
  561. pause(i)
  562. int i;
  563. {
  564.     int j;
  565.  
  566. #define SECONDSFACTOR 55
  567.     for (;  i;    i--) {
  568.     for (j=(SECONDSFACTOR*megaHz);    j;  j--);
  569.     }
  570. }
  571.  
  572. /************************************************************************/
  573. /*    putChar()                            */
  574. /************************************************************************/
  575. putChar(c)
  576. char c;
  577. {
  578.     if (thisRoom!=1 || whichIO==CONSOLE)  putCh(c);
  579. }
  580.  
  581. /************************************************************************/
  582. /*    receive() gets a modem character, or times out ...        */
  583. /*    Returns:    char on success else ERROR            */
  584. /************************************************************************/
  585. int receive(seconds)
  586. int  seconds;
  587. {
  588.     unsigned  count;
  589.  
  590.     count = seconds * 100;
  591.     while (!interpret(pMIReady)  &&  --count)  pause(1);
  592.     if (count)    return inp(mData);
  593.  
  594.     return(ERROR);
  595. }
  596.  
  597. /************************************************************************/
  598. /*    readFile() accepts a file from modem using Ward Christensen's    */
  599. /*    protocol.  (ie, compatable with xModem, modem7, yam, modem2...) */
  600. /*    Returns:    TRUE on successful transfer, else FALSE     */
  601. /************************************************************************/
  602. char readFile(pc)
  603. int  (*pc)();    /* pc will accept the file one character at a time.    */
  604.         /* returns ERROR on any problem, and closes the file    */
  605.         /* when handed ERROR as an argument.            */
  606. {
  607.     int  i, firstchar, lastSector, thisSector, thisComplement, tries;
  608.     int  toterr, checksum;
  609.     char badSector, writeError;
  610.     char sectBuf[SECTSIZE];
  611.     char *nextChar;
  612.  
  613.     if (!getYesNo("Ready for WC transfer"))   return FALSE;
  614.  
  615.     lastSector    = 0;
  616.     tries    = 0;
  617.     toterr    = 0;
  618.     writeError    = FALSE;
  619.  
  620.     while (interpret(pMIReady))   inp(mData);    /* clear garbage    */
  621.  
  622.     printf("Awaiting #0 (Try=0, Errs=0)  \r");
  623.  
  624.     do {
  625.     badSector = FALSE;
  626.  
  627.     /* get synchronized: */
  628.     do {
  629.         firstchar = receive(10);
  630.     } while (
  631.         firstchar != SOH &&
  632.         firstchar != EOT &&
  633.         firstchar != ERROR
  634.     );
  635.  
  636.     if (firstchar == ERROR)   badSector = TRUE;
  637.  
  638.     if (firstchar == SOH)  {
  639.         /* found StartOfHeader -- read sector# in: */
  640.         thisSector        = receive (1);
  641.         thisComplement    = receive (1);    /* 1's comp of thisSector */
  642.  
  643.         if ((thisSector + thisComplement) != 0xFF)    badSector = TRUE;
  644.         else {
  645.         if (thisSect == lastSector +1) {
  646.             /* right sector... let's read it in */
  647.             checksum    = 0;
  648.             nextChar    = sectBuf;
  649.             for (i=SECTSIZE;  i;  i--) {
  650.             *nextChar    = receive (1);
  651.             checksum    = (checksum + *nextChar++) & 0xFF;
  652.             }
  653.  
  654.             if (checksum != receive (1))  badSector = TRUE;
  655.             else {
  656.             tries        = 0;
  657.             lastSector    = thisSector;
  658.  
  659.             printf("Awaiting #%d (Try=0, Errs=%d)  \r",
  660.                 thisSector, toterr
  661.             );
  662.  
  663.             if (tries && toterr) putchar('\n');
  664.  
  665.             /* write sector to where-ever: */
  666.             nextChar = sectBuf;
  667.             for (i=SECTSIZE;  i;  i--) {
  668.                 writeError     &= (*pc)(*nextChar++) == ERROR;
  669.             }
  670.             if (!writeError)  outMod(ACK);
  671.             }
  672.         } else    {
  673.             /* not expected sector... */
  674.             if (thisSector != lastSector)  badSector = TRUE;
  675.             else {
  676.             /* aha -- sender missed an ACK and resent last: */
  677.             do;  while (receive(1) != ERROR);   /* eat it */
  678.             outMod(ACK);    /* back in synch! */
  679.             }
  680.         }
  681.         }    /* end of "if (thisSector + thisComplement == 255"    */
  682.     }    /* end of "if (firstChar == SOH)"            */
  683.  
  684.     if (badSector)    {
  685.         tries++;
  686.         if (lastSector != 0)  toterr++;
  687.  
  688.         while (receive (1) != ERROR);
  689.         printf("Awaiting #%d (Try=%d, Errs=%d)  \r",
  690.         lastSector, tries, toterr
  691.         );
  692.         if (tries && toterr) putchar('\n');
  693.         outMod(NAK);
  694.     }
  695.     } while (
  696.     firstchar != EOT       &&
  697.     tries      <  ERRORMAX  &&
  698.     !writeError
  699.     );
  700.  
  701.     if (firstchar != EOT   ||    tries >= ERRORMAX) {
  702.     printf("\naborting\n");
  703.     return FALSE;
  704.     } else {
  705.     outMod(ACK);
  706.     printf("\nfile reception complete.\n");
  707.     return TRUE;
  708.     }
  709. }
  710.  
  711. /************************************************************************/
  712. /*    ringSysop() signals a chat mode request.  Exits on input from    */
  713. /*  modem or keyboard.                            */
  714. /************************************************************************/
  715. ringSysop() {
  716.     char BBSCharReady();
  717.     int  i;
  718.  
  719.     mprintf("\n Ringing sysop.\n ");
  720.  
  721.     for (i=0;  !BBSCharReady() && !KBReady();  i = ++i % 7)  {
  722.     /* play shave-and-a-haircut/two bits... as best we can: */
  723.     oChar(BELL);
  724.     pause(shave[i]);
  725.     }
  726.     if (KBReady())   {
  727.     getCh();
  728.     whichIO = CONSOLE;
  729.     interact();
  730.     whichIO = MODEM;
  731.     }
  732. }
  733.  
  734. /************************************************************************/
  735. /*    sendWCChar() sends a file using Ward Christensen's protocol.    */
  736. /*    (i.e., compatable with xModem, modem7, modem2, YAM, ... )    */
  737. /*                                    */
  738. /* C is an old, tired language which does not support coroutines    */
  739. /* adequately.    SendWCChar() has suffered as a result.    SendWCChar()    */
  740. /* and the disk-read routines pass control back and forth during    */
  741. /* transmission.  Being smaller, sendWCChar() is the one which gets to    */
  742. /* stash all its variables as globals and play at subroutine.  The    */
  743. /* ugliest part is finding our place again each time we are called.    */
  744. /* SFRunning is TRUE normally and FALSE while we're finding our place.    */
  745. /* The usual indentation rules are deliberately suppressed in the case    */
  746. /* of SFRunning, to leave the "real" program structure as untouched as    */
  747. /* possible.  Ignore the righthand SFRunning stuff and it will look ok. */
  748. /*   Returns: FALSE for another char, TRUE on success, ERROR on abort    */
  749. /************************************************************************/
  750. int sendWCChar(c)
  751. int c;           /* character to output to MODEM, or ERROR on EOF */
  752. {
  753. /*  Declared globally:                        */
  754. /*  char SFcheckSum, SFeofSeen, SFRunning;            */
  755. /*  int  SFi, SFthisChar, SFthisSector, SFerrorCount, SFtries;    */
  756.  
  757.                         if (outFlag) return ERROR;
  758.                         if (SFRunning) {
  759.     while (interpret(pMIReady))  inp(mData);    /* clear garbage off line */
  760.  
  761.     SFtries    = 0;
  762.     SFerrorCount= 0;
  763.  
  764.     printf("\n0Sending #0 (Try=0 Errs=0)  \n");
  765.  
  766.     while ((receive (10) != NAK)  &&  (SFtries < RETRYMAX)) {
  767.     SFtries++;
  768.     printf("\n1Sending #0 (Try=%d Errs=0)  \r", SFtries);
  769.     }
  770.  
  771.     if (SFtries  >= RETRYMAX)  {
  772.     printf("\nTimed out awaiting initial NAK\n");
  773.     outFlag = OUTSKIP;
  774.     return ERROR;
  775.     }
  776.  
  777.     SFtries    = 0;
  778.     SFthisSector= 1;
  779.     SFthisChar    = c;
  780.     SFeofSeen    = (c == ERROR);
  781.  
  782.     /* send the file: */            } /* SFRunning block */
  783.     while (!SFeofSeen) {
  784.                         if (SFRunning) {
  785.     SFtries = 0;
  786.                         } /* SFRunning block */
  787.     /* send sector and  look for ACK: */
  788.     do {
  789.                         if (SFRunning) {
  790.         printf("\n2Sending #%d (Try=%d Errs=%d)  \n",
  791.             SFthisSector, SFtries, SFerrorCount
  792.         );
  793.         if (SFtries && SFerrorCount) putchar('\n');
  794.  
  795.         /* send one sector: */
  796.  
  797.         /* maybe read next sector into SFBuf: */
  798.         SFi =SECTSIZE;            } /* SFRunning block    */
  799.         if (!SFtries) {
  800.         for (;    SFRunning ? SFi-- : TRUE;  ) {
  801.                             if (SFRunning) {
  802.                             SFRunning  = FALSE; /*
  803.             SFthisChar = nextIn();        */ return FALSE;
  804.                             }
  805.                             SFthisChar = c;
  806.                             SFRunning = TRUE;
  807.  
  808.             if (SFthisChar == ERROR) {
  809.             SFeofSeen   = TRUE;
  810.             SFthisChar  = CPMEOF;
  811.  
  812.             /* if EOF is first char in block, don't send it: */
  813.             if (SFi == (SECTSIZE-1))   break;
  814.             }
  815.             SFBuf[SFi]        = SFthisChar;
  816.         }
  817.         }
  818.  
  819.         if (SFi == (SECTSIZE-1))   {
  820.         /* don't send empty sector just for EOF: */
  821.         break;
  822.         } else {
  823.         /* mail sector out: */
  824.         outMod(SOH        );
  825.         outMod( SFthisSector);
  826.         outMod(~SFthisSector);
  827.  
  828.         for (SFi=SECTSIZE, SFcheckSum=0;  SFi--;  ) {
  829.             SFcheckSum        = (SFcheckSum + SFBuf[SFi]) & 0xFF;
  830.             outMod( SFBuf[SFi] );
  831.         }
  832.         outMod(SFcheckSum);
  833.  
  834.         while (interpret(pMIReady))  inp(mData);    /* clear off line */
  835.  
  836.         SFtries++;
  837.         SFerrorCount++;
  838.         }
  839.     } while ((receive(10) != ACK)  &&  (SFtries < RETRYMAX));
  840.     SFthisSector++;
  841.     SFerrorCount--;
  842.  
  843.     if (SFtries >= RETRYMAX)   break;
  844.     }
  845.  
  846.     /* file sent, or not, as the case may be: */
  847.     if (SFtries >= RETRYMAX)  {
  848.     printf("\nNo ACK on sector, aborting\n");
  849.     outFlag = OUTSKIP;
  850.     return ERROR;
  851.     } else {
  852.     /* tell recipient we're all finished: */
  853.     SFtries = 0;
  854.     do {
  855.         outMod(EOT);
  856.         while (interpret(pMIReady))  inp(mData);    /* clear off line */
  857.         SFtries++;
  858.     } while ((receive(10) != ACK)  &&  (SFtries < RETRYMAX));
  859.  
  860.     if (SFtries < RETRYMAX)  {
  861.         return TRUE;
  862.     } else {
  863.         printf("\nNo ACK on EOT, aborting\n");
  864.         outFlag = OUTSKIP;
  865.         return ERROR;
  866.     }
  867.     }
  868. }
  869.