home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 12: Textmags & Docs / nf_archive_12.iso / MAGS / DOCS / MODEMIO.ZIP / MODEMIO.DOC
Encoding:
Internet Message Format  |  1991-10-17  |  26.3 KB

  1. From: steve@thelake.mn.org (Steve Yelvington)
  2. Newsgroups: comp.sys.atari.st.tech
  3. Subject: DOC: Writing communications software
  4. Date: 13 Oct 91 03:15:54 GMT
  5. Organization: The little computer in the little town that time forgot
  6. Lines: 760
  7.  
  8. It's been a few weeks since I posted the original version of this, and
  9. there have been some additions and changes. As usual, corrections/
  10. suggestions/complaints/questions are appreciated.
  11.  
  12.   0/ cut here
  13. ==0\================================================================
  14.  
  15. The Atari ST modem programming guide
  16. October 12, 1991
  17.  
  18. Copyright 1991 Steve Yelvington
  19.     Internet: <steve@thelake.mn.org>
  20.     GEnie:    S.YELVINGTO2
  21.  
  22.  
  23. Introduction
  24. ------------
  25.  
  26. Modem i/o on the ST is isn't difficult. In fact, compared with other
  27. personal systems, the ST is unusually easy to program for serial
  28. communications.
  29.  
  30. The hard work is done for you. TOS includes interrupt-driven serial
  31. port routines. This means that when a character arrives at the serial
  32. port, TOS breaks away from whatever it's doing, grabs the character,
  33. stores it in a buffer, and returns to the task that was interrupted.
  34. You don't need to write code to do that.
  35.  
  36. The serial port buffers -- one for input, one for output -- are FIFO
  37. (First In, First Out) queues. TOS provides functions for reading from
  38. the input buffer, writing to the output buffer, and even changing the
  39. size of the buffers.
  40.  
  41. All you have to do is read from or write to the serial port as if it
  42. were a console.
  43.  
  44. This document contains examples in C. If you're programming in another
  45. language, don't despair, because C is used here only to string together
  46. TOS functions. The strategies don't change when you shift to another
  47. system of notation.
  48.  
  49. The modem i/o routines
  50. ----------------------
  51.  
  52. Both BIOS and GEMDOS provide functions for single-character serial
  53. i/o that work exactly like their console equivalents.
  54.  
  55. >From GEMDOS:
  56.  
  57.    Console      Serial         Description
  58.    -------      ------         -----------
  59.    Cconin                      Read a character with echo
  60.    Cnecin       Cauxin         Read a character without echo
  61.    Cconout      Cauxout        Write a character
  62.    Cconis       Cauxis         Return TRUE if a character is available         
  63.    Cconos       Cauxos         Return TRUE if output is possible, i.e.,
  64.                                the output buffer has room for more data
  65.  
  66. There is not a serial port equivalent for Cconws, but you should be
  67. able to write one easily enough:
  68.  
  69. #include <osbind.h>
  70. void Cauxws(s) /* write a nul-terminated string to the aux port */
  71.     register char *s;
  72.     {
  73.     register char c;
  74.     while (c=*s++)
  75.         Cauxout(c);
  76.     }
  77.  
  78.  
  79. In C, GEMDOS functions usually are implemented as preprocessor
  80. macros that translate into gemdos() function calls. Other
  81. languages may require that you write GEMDOS calls explicitly. If
  82. you're coding in assembler, you should be able to translate the
  83. following by keeping in mind that you push the arguments onto
  84. the stack from right to left and then call trap #1 for gemdos.
  85.  
  86. Here are common C preprocessor definitions for the
  87. above-mentioned operations (plus a few more).
  88.  
  89. #define Cconin()    (long)    gemdos(0x1)
  90. #define Cconout(a)    (void)    gemdos(0x2,(short)(a))
  91. #define Cauxin()    (short) gemdos(0x3)
  92. #define Cauxout(a)    (void)    gemdos(0x4,(short)(a))
  93. #define Cprnout(a)    (void)    gemdos(0x5,(short)(a))
  94. #define Cnecin()    (long)    gemdos(0x8)
  95. #define Crawio(a)    (long)    gemdos(0x6,(short)(a))
  96. #define Crawcin()    (long)    gemdos(0x7)
  97. #define Cconws(a)    (void)    gemdos(0x9,(char*)(a))
  98. #define Cconrs(a)    (void)    gemdos(0x0a,(char*)(a))
  99. #define Cconis()    (short) gemdos(0x0b)
  100. #define Cconos()    (short) gemdos(0x10)
  101. #define Cprnos()    (short) gemdos(0x11)
  102. #define Cauxis()    (short) gemdos(0x12)
  103. #define Cauxos()    (short) gemdos(0x13)
  104.  
  105.  
  106. What about BIOS?
  107.  
  108. >From BIOS, the approach is slightly different. BIOS provides one
  109. function for each operation, with the device indicated by an
  110. argument. The first argument to the function is the device
  111. number: 
  112.  
  113.    Console      Serial         Description
  114.    -------      ------         -----------
  115.    Bconin(2)    Bconin(1)      Read a character
  116.    Bconout(2,c) Bconout(1,c)   Write a character c
  117.    Bconstat(2)  Bconstat(1)    Return TRUE if a character is available         
  118.    Bcostat(2)   Bcostat(1)     Return TRUE if output is possible
  119.  
  120. Note that the device numbers used by the BIOS are different from the
  121. file handles used at the GEMDOS level. The BIOS device numbers may be
  122. defined in this fashion:
  123.  
  124. #define BCON_PRT    0 /* printer */
  125. #define BCON_AUX    1 /* aux (modem) port */
  126. #define BCON_CON    2 /* console */
  127. #define BCON_MIDI    3 /* MIDI port */
  128. #define BCON_KBD    4 /* intelligent keyboard device output */
  129. #define BCON_RAW    5 /* raw console output, no VT52 */
  130.  
  131. The BIOS macros are defined as follows:
  132.  
  133. #define Bconstat(dev)     bios(1,(short)(dev))
  134. #define Bconin(dev)    bios(2,(short)(dev))
  135. #define Bconout(dev,ch)    bios(3,(short)(dev),(short)(ch))
  136. #define Bcostat(dev)    bios(8,(short)(dev))
  137.  
  138. If you're using assembly language, the bios trap is #13.
  139.  
  140. There is a no function to read a string from the serial port at
  141. either the GEMDOS level or the BIOS level. For reasons that will
  142. become clear shortly, you should write your own.
  143.  
  144. All of the GEMDOS and BIOS input functions return 32-bit LONG values.
  145.  
  146. In the case of the serial port, only the low 8 bits are interesting.
  147.  
  148. In the case of the console, the low 8 bits will contain the
  149. ASCII code corresponding to the key -- if there is one. If the
  150. keystroke is a nonstandard key, such as a function key, HELP or
  151. UNDO, the low eight bits will be an ASCII nul value. In all
  152. cases, the upper 24 bits contain a unique keyscan code and
  153. information regarding the state of the shift keys.
  154.  
  155. A simple terminal program
  156. ------------------------- 
  157.  
  158. A ``dumb'' terminal program using these functions is simple to write.
  159. All you need is a loop in which you will ``poll'' the console and the
  160. serial port. It might look like this:
  161.  
  162. #include <osbind.h>
  163. #define BANNER "\033EIncredibly dumb full-duplex terminal version 0.1\r\n"
  164.  
  165. main()
  166.     {
  167.     int c;
  168.     Cconws(BANNER);
  169.     for(;;)
  170.         {
  171.         if (Cauxstat()) /* data waiting at the aux port */
  172.             {
  173.             c = (int) (Cauxin() & 0xFF); /* get it and clean it */
  174.             Cconout(c);
  175.             }
  176.         if (Cconstat()) /* data waiting at the console */
  177.             {
  178.             c = (int) (Cnecin() & 0xFF); /* get it and clean it */
  179.             if (c) /* if it's not a NUL from a function key */
  180.                 Cauxout(c);
  181.             }
  182.         }
  183.     }
  184.  
  185. You'll notice that the above example runs forever. Well, not quite.
  186. GEMDOS console i/o processes control-characters, and a control-C will
  187. kill the program.
  188.  
  189. Of course, that means you can't send control-C out the modem port.
  190. That's one of many reasons it's called an ``incredibly dumb''
  191. terminal.
  192.  
  193. You'll probably want to rewrite the program using BIOS functions and
  194. test for use of the UNDO or F10 keys.
  195.  
  196.  
  197. Whatever happened to GEM?
  198. -------------------------
  199.  
  200. None of this discussion has dealt with GEM. Why? Because so far as
  201. GEM is concerned, the serial port doesn't exist.
  202.  
  203. The AES Event Manager doesn't poll the aux port to determine whether
  204. an event has occurred.
  205.  
  206. If you want to write a GEM event-driven communications program that
  207. will cooperate with desk accessories for multitasking, you'll need to
  208. use the timer with a time value of 0. This will allow a task switch
  209. to take place, and when you regain control you can check the serial
  210. port's status with GEMDOS or BIOS.
  211.  
  212. If you're worried that the serial input buffer will overflow while
  213. some system-hog task is running, resize the buffer as described
  214. below.
  215.  
  216. The real world intrudes
  217. -----------------------
  218.  
  219. None of the above functions cares whether the modem is live or dead.
  220. They only deal with data. If your caller falls asleep at the keyboard
  221. or abruptly hangs up, the above functions won't worry about a thing.
  222. If you're writing a communications program or a BBS, you'll have to
  223. deal with these real-world conditions. Modems and phone lines are not
  224. perfect.
  225.  
  226. There are many ways to handle such errors.  In C, you might want to
  227. use the setjmp and longjmp functions to ``bail out'' and restart your
  228. program. Assuming you have set up a jump buffer called ``panic,'' you
  229. might write a low-level aux port input function that looks like this:
  230.  
  231. #include <time.h>
  232. #include <osbind.h>
  233. #define TIMEOUT        60    /* 60-second timeout */
  234.  
  235. int auxin()
  236.     {
  237.     clock_t time1,elapsed;
  238.     if (!rs232cd())
  239.         {
  240.         hangup();
  241.         longjmp(panic);
  242.         }
  243.     time1 = clock();  /* get tick time */
  244.  
  245.     /* loop for up to TIMEOUT seconds, waiting for aux input */
  246.     for(;;)
  247.         {
  248.         if (Cauxis()) /* these could just as well be BIOS calls */
  249.             return Cauxin();
  250.  
  251.         /* no activity yet, so ... */
  252.         /* get new tick time and convert to seconds */
  253.  
  254.         elapsed = (clock() - time1) / CLK_TCK;
  255.  
  256.         /* if too much time has elapsed, restart */
  257.  
  258.         if (elapsed > TIMEOUT)
  259.             {
  260.             hangup();
  261.             longjmp(panic);
  262.             }
  263.         } /* continue the loop */    
  264.     }
  265.  
  266.  
  267. The above code has used a couple of functions that aren't in the
  268. library and bear some explaining. Here is the function that checks
  269. the carrier-detect status of the aux port by examining a hardware
  270. register:
  271.  
  272. #include <osbind.h>
  273. int rs232cd()            /* state of rs232 carrier detect line */
  274.     {
  275.     register long ssp;
  276.     register int *mfp, status;
  277.     mfp = ((int *) 0xFFFFFA00L);        /* base address of MFP */
  278.     ssp = Super(0L);            /* enter supervisor mode */
  279.     status = *mfp;                /* get MFP status */
  280.     Super(ssp);                /* return to user mode */
  281.     return(!(status & 0x0002));        /* check for carrier */
  282.     }
  283.  
  284.  
  285. Here is a function that hangs up the phone line by dropping the DTR
  286. (Data Terminal Ready) signal. Note that most modems are shipped from
  287. the factory with DTR sensitivity disabled. You should be able to turn
  288. it back on by toggling a DIP switch or with a command from the
  289. keyboard.
  290.  
  291.  
  292. #include <osbind.h>
  293. void hangup()
  294.     {
  295.     Ongibit(0x10);    /* dtr off */
  296.     sleep(1);    /* wait one second */
  297.     Offgibit(0xEF);    /* dtr on */
  298.     }
  299.  
  300. Now, what about getting a string from the modem port? You'll need to
  301. call your custom auxin() function, as above. Here is an auxgets
  302. function modified from the dLibs getln. Note that it uses a function
  303. ``auxput'' for output; you can rewrite or #define it to mirror the
  304. BIOS or GEMDOS-level routine you used for input:
  305.  
  306. #define    KEY_CR        0x0D /* carriage return */
  307. #define    KEY_LF        0x0A /* linefeed */
  308. #define    KEY_BS        0x08 /* backspace */
  309. #define    KEY_DEL        0x7F /* delete */
  310.  
  311. char *auxgets(buffer, limit)
  312.     char *buffer; /* where keystrokes will be stored */
  313.     register int limit; /* size of the buffer */
  314.     {
  315.     register char *bp = buffer;
  316.     register int c, i = 0;
  317.     for(;;)
  318.         {
  319.         c = auxin() & 0xFF; /* mask off high bits, just in case */
  320.  
  321.         /* end of line */
  322.         if((c == KEY_CR) || (c == KEY_LF)) 
  323.             {
  324.             *bp = '\0';
  325.             break;
  326.             }
  327.  
  328.         /* backspace or delete */
  329.         else if(((c == KEY_BS) || (c == KEY_DEL)) && (bp != buffer))
  330.             {
  331.             --bp;
  332.             auxput('\b');
  333.             auxput(' ');
  334.             auxput('\b');
  335.             --i;
  336.             }
  337.         else if((c >= ' ') && (i < limit))
  338.             {
  339.             auxput(*bp++ = c);
  340.             ++i;
  341.             }
  342.         }
  343.     return(buffer);
  344.     }
  345.  
  346.  
  347.  
  348. Configuring the serial port
  349. ---------------------------
  350.  
  351. In order to communicate with another machine over the serial port,
  352. speed and flow-control settings may need to be altered. Atari's XBIOS
  353. (Extended BIOS) provides a function that can configure the serial
  354. port. The function Rsconf sets the speed, flow-control and four
  355. bitmapped hardware registers. 
  356.  
  357. Speed (bits per second, often incorrectly called baud) can be set to
  358. any of 16 values:
  359.  
  360. #define BPS_19200    0 /* standard */
  361. #define BPS_9600    1 /* standard */
  362. #define BPS_4800    2 /* standard */
  363. #define BPS_3600    3
  364. #define BPS_2400    4 /* standard */
  365. #define BPS_2000    5
  366. #define BPS_1800    6
  367. #define BPS_1200    7 /* standard */
  368. #define BPS_600        8
  369. #define BPS_300        9 /* standard */
  370. #define BPS_200        10
  371. #define BPS_150        11
  372. #define BPS_134        12
  373. #define BPS_110        13
  374. #define BPS_75        14
  375. #define BPS_50        15
  376.  
  377. Only the values marked /* standard */ are commonly used in modem
  378. communications.
  379.  
  380. If you are using a slow- or medium-speed modem (under 9600bps) you
  381. probably will need to match the ST's serial port speed to that of the
  382. remote system.
  383.  
  384. If you are using a high-speed modem, you may be able to set the speed
  385. at 19,200bps and let the modem handle speed-matching chores. In this
  386. case, you probably will need to use RTS/CTS (hardware) flow-control
  387. to avoid overloading the ST's input buffer and the modem's output
  388. buffer. RTS/CTS is broken in most versions of TOS, and an /auto/
  389. folder program must be installed to fix it.
  390.  
  391. Flow control can be configured to any of four settings:
  392.  
  393. #define FLOW_NONE    0 /* default */
  394. #define FLOW_XONXOFF    1 /* control-s, control-q software */
  395. #define FLOW_RTSCTS    2 /* hardware */
  396. #define FLOW_BOTH    3 /* both hardware and software */
  397.  
  398. For binary file transfers, software flow control must not be used.
  399.  
  400. The hardware registers are not useful in ordinary modem-related
  401. programming, and altering their values incorrectly will have
  402. unpleasant side effects. Passing -1 to Rsconf will leave the
  403. corresponding setting unchanged.
  404.  
  405. Thus, to set bps rate to 2400 and flow control to none, the Rsconf
  406. function call would look like this:
  407.  
  408.     Rsconf(BPS_1200,FLOW_NONE,-1,-1,-1,-1)
  409.  
  410.  
  411. Parity and duplex
  412. -----------------
  413.  
  414. Parity is not commonly used in communicating with personal computers,
  415. but you may need to deal with it when communicating with a mainframe.
  416.  
  417. The idea behind parity is to provide at least a small degree of
  418. assurance that the transmitted character is the same as the received
  419. character. The way this is done is by fiddling with the high bit of
  420. each byte, which ordinarily would be zero for an ASCII character.
  421.  
  422. ``Even parity'' means that the high bit is either set or unset, as
  423. necessary, to ensure that the result is an even number. ``Odd
  424. parity'' means the opposite. You can test this with the modulus
  425. operator in C, then strip the high bit by ANDing the byte with 0x7F
  426. or set the high bit by ANDing with 0xFF.
  427.  
  428. TOS also can generate parity values automatically if the proper
  429. hardware register is set. However, setting the register requires that
  430. you also worry about start/stop bits, word length and the serial
  431. hardware frequency, which is a can of worms.
  432.  
  433. Duplex is a potentially confusing term that describes whether you or
  434. a remote system is responsible for putting a character on your
  435. screen.
  436.  
  437. Most bulletin board systems run ``full duplex,'' which means that a
  438. keystroke from a caller is sent to the BBS, which then echoes that
  439. character back to the caller. This relationship is not symmetrical.
  440. The caller doesn't echo anything back to the BBS, because the echoes
  441. would be interpreted as commands.
  442.  
  443. ``Half duplex'' means that each party is responsible for managing his
  444. or her own display. GEnie normally runs half duplex. Neither caller
  445. nor BBS echoes anything.
  446.  
  447. If you're writing a BBS or a terminal program with a ``chat mode,''
  448. you may want to include a ``both local and remote echo'' option. This
  449. would allow the remote to run full duplex while you manage both your
  450. screen and the caller's screen.
  451.  
  452.  
  453. Resizing the serial buffers
  454. ---------------------------
  455.  
  456. When a character arrives at the serial port, TOS interrupts what
  457. it's doing, grabs the character before it disappears, and
  458. records it in an input buffer. When your program wishes to write
  459. to the serial port, TOS buffers the output so that your program
  460. can proceed with other chores without having to wait for a slow
  461. modem to transmit the data.
  462.  
  463. TOS provides those buffers, but TOS also provides a facility for
  464. changing them. Why would you want to do so? Here's a scenario:
  465.  
  466. You're using Xmodem and you have a slow floppy drive. Xmodem
  467. ACKS a block, then proceeds to write it to disk while the sender
  468. sends the next block. So far, so good. But if you switch to
  469. Ymodem's 1K blocks and a very fast modem, the TOS default buffer
  470. might overflow while you're writing the previous block to that
  471. slow floppy drive. Solution: Use a bigger input buffer.
  472.  
  473. Another scenario: You're running a BBS. The caller is reading a
  474. long message and decides it's boring. The caller pushes the
  475. cancel key (control-C, or S, or whatever you've decided makes
  476. sense.) The BBS stops sending, but that big output buffer is
  477. full of data -- and it just keeps coming, and coming ....
  478. Solution: Use small i/o buffers for interacting with humans.
  479.  
  480. TOS provides an XBIOS function that returns a pointer to an
  481. input buffer record. It requires one argument: The BIOS number
  482. of a device (see above).
  483.  
  484. #define Iorec(dev) (void*) xbios(14, (short)(dev))
  485.  
  486. Assembly programmers: The xbios trap is #14, so push the device
  487. number followed by two 14's, then do the trap. The pointer will
  488. be found in d0.
  489.  
  490. The iorec structure is defined as follows:
  491.  
  492. struct iorec
  493.     {
  494.     char *buf;    /* pointer to an array: char buffer[bufsiz] */
  495.     short bufsiz; /* size of the array         */
  496.     short head;   /* index for writing         */
  497.     short tail;   /* index for reading         */
  498.     short low;    /* low water mark, for XON   */
  499.     short high;   /* high water mark, for XOFF */
  500.     }
  501.  
  502. For the AUX port, the output buffer record immediately follows
  503. the input buffer record.
  504.  
  505. Before you go poking around inside these records, keep in mind
  506. that they are internal to TOS, and they are shared by all
  507. processes that run on your system. You can change the buffers
  508. and even bypass GEMDOS and BIOS for reading and writing, if you
  509. so desire, but you must be prepared to live with the
  510. consequences.
  511.  
  512. In particular, be very careful to restore the original record
  513. when your program terminates (unless you're writing a TSR with
  514. the express purpose of enlarging a buffer).
  515.  
  516. Immediately after the serial port's input and output records are
  517. some internal TOS variables that may be useful. The following
  518. code has worked on all versions of TOS that I've encountered. I
  519. don't know whether Atari guarantees that the variables won't be
  520. changed on future versions of TOS.
  521.  
  522. With those cautions in mind, here is some code, originally
  523. lifted from an early ST communications program written by Dale
  524. Schumacher. I've been using the open_aux and close_aux functions
  525. for years, but I haven't had a need to deal with the rsr, tsr,
  526. and flow control variables.
  527.  
  528. typedef struct 
  529.     {
  530.     struct iorec in;    /*  0 Input buffer record  */
  531.     struct iorec out;    /* 14 Output buffer record */
  532.     char rsr_status;    /* 28 MFP(0x1C) Receiver status    */
  533.     char tsr_status;    /* 29 MFP(0x1D) Transmit status    */
  534.     char xoff_sent;        /* 30 TRUE if we sent XOFF    */
  535.     char xoff_received;    /* 31 TRUE if we got XOFF    */
  536.     char mode;        /* 32 Bit 0 XON, Bit 1 RTS mode    */
  537.     char filler;        /* 33 Unused            */
  538.     } IOREC;
  539.  
  540. #define    IBUFSIZ    1200
  541. #define    OBUFSIZ    2
  542.  
  543. char    st_ibuf[IBUFSIZ];    /* our own input buffer        */
  544. char    st_obuf[OBUFSIZ];    /* and our own output buffer    */
  545.  
  546. IOREC    *st_sysr;        /* ptr to system rs232 record    */
  547. IOREC    st_savr;        /* to save system rs232 record    */
  548.  
  549. IOREC    st_myiorec = 
  550.     {
  551.     /* first, an input record */
  552.     st_ibuf, IBUFSIZ, 0, 0, (IBUFSIZ/4), (3*(IBUFSIZ/4)),
  553.  
  554.     /* then an output record */
  555.     st_obuf, OBUFSIZ, 0, 0, 0, 1,
  556.  
  557.     /* and the rsr, tsr, flow control stuff */
  558.     0, 0, 0, 0, 0, 0
  559.     };
  560.  
  561. void openaux()        /* set up our own rs232 input and output buffers */
  562.     {
  563.     while(Bconstat(AUX))        /* flush existing buffer */
  564.         Bconin(AUX);
  565.     st_sysr = (IOREC *)Iorec(0);
  566.     st_savr = *st_sysr;        /* Save system buffer    */
  567.     *st_sysr = st_myiorec;        /* Set my io buffer    */
  568.     }
  569.  
  570. void closeaux()        /* restore system i/o buffer */
  571.     {
  572.     *st_sysr = st_savr;
  573.     }
  574.  
  575.  
  576. Take a break
  577. ------------
  578.  
  579. Some remote systems, notably Unix sites, need a BREAK signal to
  580. wake them up and help them synchronize with a caller's bps rate.
  581. To generate a BREAK on the ST, you briefly toggle the transmit
  582. status register. Here is code that does so by directly accessing
  583. the hardware on the ST. I think Dale Schumacher is the author of
  584. this code.
  585.  
  586. #include <time.h>
  587. #include <osbind.h>
  588.  
  589. void nap(n)            /* do nothing for n-100ths of a second */
  590.     register int n;
  591.     {
  592.     register clock_t t, dt;
  593.     dt = (CLK_TCK * ((clock_t) n)) / ((clock_t) 100);
  594.     t = clock() + dt;
  595.     while(clock() < t)
  596.         ;
  597.     }
  598.  
  599. void snd_brk()        /* send a BREAK to the rs232 port */
  600.     {
  601.     register long ssp;
  602.     register char *tsr;
  603.     tsr = ((char *) 0xFFFFFA2DL);        /* address of tsr */
  604.     ssp = (long) Super(0L);            /* enter supervisor mode */
  605.     *tsr = 0x09;                /* turn break on */
  606.     Super(ssp);                /* return to user mode */
  607.     nap(30);                /* 0.3 second BREAK */
  608.     ssp = (long) Super(0L);            /* enter supervisor mode */
  609.     *tsr = 0x01;                /* turn break off */
  610.     Super(ssp);                /* return to user mode */
  611.     }
  612.  
  613. The clock() function should be available with most C compilers.
  614. If you're writing in a language that doesn't have an equivalent,
  615. the trick is simply to peek at memory location 4BA (hex), which
  616. is the 200Hz system timer. You must be in supervisor mode to do
  617. so. The value is a 32-bit long.
  618.  
  619.  
  620. File transfers
  621. --------------
  622.  
  623. When transferring a binary file, the sender and the receiver
  624. have to agree on a method for signalling the end of the
  625. transmission. Ideally, they should have some method of
  626. determining whether the file was mangled by line noise. And if
  627. it was, there should be a way to retransmit the bad data.
  628.  
  629. The methods for solving these problems are called file-transfer
  630. protocols, and their name should be legion, for they are many.
  631. Xmodem, Ymodem, Zmodem, Quick B, WXmodem, Kermit, Punter, FAST,
  632. Telink, UUCP 'G' ... the list goes on.
  633.  
  634. Most of them follow the same basic strategy. A file is broken up
  635. into multiple packets. Each packet starts with a header that
  636. includes a packet number and a mathematical value, called a
  637. checksum, that can be used to determine whether the data in the
  638. packet has been corrupted.
  639.  
  640. XMODEM is a very simple implementation of this strategy.
  641.  
  642. It uses 128-byte data packets. It uses packet headers consisting
  643. of a a byte for the packet number, and (in the original version)
  644. a byte for a checksum. The checksum is calculated by adding the
  645. numeric value of each byte and throwing away the overflow. It's
  646. not foolproof, but it detects most errors.
  647.  
  648. Later versions of Xmodem use a 16-bit ``cyclical redundancy
  649. check'' calculated by using a fairly complex algorithm. It's
  650. much harder to fool.
  651.  
  652. If the Xmodem receiver adds up all the values and finds they
  653. don't match the total that was encoded in the packet header, it
  654. sends a NAK (Negative AcKnowledgement) signal to the sender,
  655. which then retransmits the corrupted packet. If they match, it
  656. the receiver sends an ACK.
  657.  
  658. Because the packages are small, and because the sender always waits
  659. for an ACK before proceeding, Xmodem is not particularly fast. When
  660. XMODEM runs over a network that does its own error-checking and
  661. packet operations internally (such as CompuServe or GEnie's data
  662. systems), Xmodem can get painfully slow. That lonely ACK signal
  663. can take a long time to wander through the network and get back
  664. to you.
  665.  
  666. There are two ways of dealing with Xmodem's speed problems. One is to
  667. use bigger packets, which is the approach taken by most alternatives,
  668. including Ymodem. The other is to use ``windowing,'' which is used by
  669. WXmodem, UUCP 'G' and Zmodem.
  670.  
  671. A windowing protocol doesn't wait for an ACK before sending.
  672. Instead, it transmits the next block. If a NAK arrives, it can
  673. back up and resend the bad block. The number of blocks that can
  674. be ``remembered'' is the size of the ``window.'' A
  675. sliding-window implementation is one in which the sender's
  676. window moves along as you move through the file and get ACKs
  677. from the receiver.
  678.  
  679. And now, the code.
  680.  
  681. This document isn't going to attempt to present a C
  682. implementation of a file-transfer protocol. But the question of
  683. ``how do I calculate a checksum'' comes up frequently, here is C
  684. code to create the values used by Xmodem, Ymodem and some
  685. related programs.
  686.  
  687. Some CRC functions index the CRC values out of an array of
  688. precalculated numbers. That approach probably is faster than the
  689. one below, but the ST has plenty of horsepower for this sort of
  690. thing.
  691.  
  692. Don't ask me to explain the math. I can't. I didn't write this;
  693. I just use it.
  694.  
  695.  /*
  696.   * This function calculates the CRC used by the XMODEM/CRC
  697.   * Protocol.
  698.   * The first argument is a pointer to the message block.
  699.   * The second argument is the number of bytes in the message block.
  700.   * The function returns a short integer that contains the CRC value.
  701.   */
  702.  short crc16(ptr, count)
  703.  register char *ptr;
  704.  register short count;
  705.     {
  706.     register short crc = 0, i;
  707.     while(--count >= 0) 
  708.        {
  709.        crc = crc ^ (short)*ptr++ << 8;
  710.        for(i=0; i<8; ++i)
  711.           {
  712.           if(crc & 0x8000)
  713.              crc = crc << 1 ^ 0x1021;
  714.           else
  715.              crc = crc << 1;
  716.           }
  717.        }
  718.     return(crc & 0xFFFF);
  719.     }
  720.   
  721.  
  722. If you want to port this to another language, keep in mind that 
  723.  
  724.   a << b  evaluates to the bit pattern of a shifted left b bits
  725.  
  726.   * dereferences a pointer; *ptr++ evaluates to the pointed-to value
  727.     and then increments ptr by the size of the pointed-to value
  728.  
  729.   ^ is a bitwise exclusive OR operator; 11110000 (binary) ^ 00001111
  730.     evaluates to 11111111
  731.  
  732.   & in the context above is a bitwise AND operator; crc & 0xFFFF
  733.     basically strips anything above 16 bits (important if your
  734.     environment supports 32-bit ints).
  735.  
  736.  
  737. Acknowledgements
  738. ----------------
  739.  
  740. This document wouldn't have been possible without the assistance
  741. of the following people. Some provided instructive code samples;
  742. others caught errors in earlier versions of this document or
  743. raised important questions. Thanks to: Dale Schumacher, John
  744. Stanley, Robert Royar, Adam David, Rob McMullen, Michael
  745. Fischer.
  746.  
  747. Corrections
  748. -----------
  749.  
  750. Send corrections to steve@thelake.mn.org (Internet),
  751. S.YELVINGTO2 (GEnie), plains!umn-cs!thelake!steve (UUCP), or
  752. snail mail to P.O. Box 38, Marine on St. Croix, MN 55047 USA.
  753.  
  754. Permissions
  755. -----------
  756.  
  757. This file may be distributed only in electronic format, and only
  758. in its original, unaltered state. All other reproduction is
  759. prohibited without written permission of the author. This
  760. information is intended to be accurate, but the author makes no
  761. representation that is is free of errors. This information is
  762. presented without warranty of any sort, and the author is not
  763. responsible for any side effects. None of this information is
  764. extracted from any proprietary documents or trade secrets.
  765. PS: Shakespeare was right about the lawyers.
  766.  
  767. =================================================================
  768.  
  769. ə