home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / calculat / hpread13.zip / HPREAD.C next >
C/C++ Source or Header  |  1990-01-31  |  24KB  |  598 lines

  1. /*
  2.  
  3. HPREAD
  4. Copyright (C) 1990  Mark Adler  Pasadena, CA  All rights reserved.
  5.  
  6. This source file may be freely copied and distributed, so long as it
  7. is not modified in any way.  No part of this program may be used in
  8. any product for sale without the written permission of the author.
  9.  
  10. This program is user supported (shareware), which means that users
  11. are expected to make a (modest) contribution of $5 to the author. 
  12. The check or money order should be made out to Mark Adler, and sent
  13. to:
  14.  
  15.         Mark Adler
  16.         P.O. Box 60998
  17.         Pasadena, CA 91116
  18.  
  19. The $5 entitles you to the sense of well-being that comes from
  20. knowing that you have supported this sort of independent effort to
  21. make your life easier, and that you are encouraging the continuing
  22. enhancement of this program.  Feel free to send comments and
  23. suggestions on HPREAD to the same address, whether or not you are
  24. making the $5 contribution.
  25.  
  26. Version history -
  27.  
  28. 1.0     13 Jan 1990     First public version.
  29. 1.1     14 Jan 1990     Improve error correction and correct and
  30.                         improve the documentation.
  31. 1.2     22 Jan 1990     Defer decoding and error correction until all
  32.                         data is received, unless the new /f option is
  33.                         specified, in which case decode on the fly.
  34.                         Count successfully corrected ("fixed") bytes.
  35.                         Distinguish imbedded from trailing errors.
  36.                         Disable interrupts during transfers, new /e
  37.                         option enables.
  38.                         Check compiler and memory model during compile.
  39. 1.3     31 Jan 1990     Optionally restore the timer count from the
  40.                         real-time cmos clock (if an AT or greater) after
  41.                         a transfer.
  42.  
  43. Thanks to Paul Dujmich and Steve Haehnichen for providing test
  44. results on HPREAD, and to Paul for suggesting a schematic in the
  45. documentation.
  46.  
  47.  
  48. HPREAD reads data sent by infrared pulses from an HP calculator (such
  49. as an HP-28S) to a Radio Shack $3.49 GP1U52X Infrared Receiver
  50. (catalog #276-137).  The output pin of the receiver is connected to a
  51. a bit of an input port on an IBM PC or compatible.  HPREAD then reads
  52. from the specified port and bit and writes the received data to
  53. stdout.  The data is terminated by a pause of more than three
  54. seconds.  HPREAD also writes the number of bytes transferred and the
  55. number of unrecoverable errors to stderr.  For example:
  56.  
  57.      C>hpread >foo
  58.      1043 bytes transferred (17 fixed) with 0 errors
  59.  
  60. writes the file "foo" with the transferred data.  By default, HPREAD
  61. converts any 0x04 or 0x0A characters that the HP sends into a new
  62. line sequence (carriage return, line feed).  If the "/n" option is
  63. specified, no translation of the data is performed.
  64.  
  65. By default, HPREAD assumes that the receiver is connected to the
  66. parallel printer port's ERROR* line (pin 15 on the DB-25).  Some
  67. parallel printers supply +5V power on one of the lines of the
  68. printer-end connector.  For example, my printer (a C.Itoh 8510A)
  69. provides +5V at 50 mA on pin 18 of the 36 pin Centronics connector
  70. (NOT the DB-25).  This can truly minimize the external hardware for
  71. the detector.  Just put a switch on the ERROR* line to the computer
  72. to select either the printer's ERROR* line or the output of the
  73. detector, and connect the power and ground of the detector to the
  74. printer's ground and +5V lines.  (For this to work, of course, the
  75. printer must be on.)  Schematic:
  76.  
  77.                                SPDT switch
  78.      Printer (DB-25) pin 15 -----*      (used to go to port pin 15)
  79.      Detector Vout (pin 1) ------*<---o------ Parallel port pin 15
  80.      Detector GND (pin 3) ------------------- Parallel port pin 18
  81.      Detector Vcc (pin 2) ------------------- Printer supplied +5v
  82.      Metal case of detector ----------------- Detector GND (pin 3)
  83.  
  84. If an easy source of +5V is not available, then you only need three
  85. more parts to supply it.  An AC adapter that puts out DC anywhere in
  86. the range of +6V to +30V (whatever current it puts out is enough), a
  87. 0.047 uF or 0.1 uF capacitor, and a 7805 or 78L05 voltage regulator. 
  88. You can get all of this from (you guessed it) Radio Shack.  (No, I do
  89. not have stock in Radio Shack.  It's just it seems there are more of
  90. them than McDonald's.)  Connect the output of the AC adapter to the
  91. input of the voltage regulator with the capacitor across that (and
  92. near the regulator).  Make sure to check the polarity of the AC
  93. adapter---some put the positive lead and some put the negative lead
  94. on the outside of the connector.  Connect the output of the regulator
  95. to the detector's power and ground.  An output capacitor is not
  96. needed, since the detector has a built in 5 uF capacitor for power
  97. buffering.  Schematic:
  98.  
  99.                                SPDT switch
  100.      Printer (DB-25) pin 15 -----*      (used to go to port pin 15)
  101.      Detector Vout (pin 1) ------*<---o------ Parallel port pin 15
  102.      Detector GND (pin 3) ------------------- Parallel port pin 18
  103.      Adapter +DC--------|---Regulator In
  104.               0.047 uF ===  Regulator Out---- Detector Vcc (pin 2)
  105.      Adapter -DC--------|---Regulator GND---- Detector GND (pin 3)
  106.      Metal case of detector ----------------- Detector GND (pin 3)
  107.  
  108. Wherever power comes from, be sure to connect the metal case of the
  109. detector to ground to reduce noise in the detector.
  110.  
  111. If a different port or bit is used, then the port and bit mask (and
  112. possibly an invert option) must be specified on the HPREAD command
  113. line.  For example:
  114.  
  115.      C>hpread 22e2 1 /i >foo
  116.      1043 bytes transferred (13 fixed) with 0 errors
  117.  
  118. reads the data from port 0x22e2, bit 0, with the input bit inverted.
  119. Both the port and the mask must be specified in hexadecimal.  The bit
  120. mask must be one of 1, 2, 4, 8, 10, 20, 40, or 80.  If there are
  121. other operations necessary to initialize the port before it can be
  122. used, then another program could be written to do the initialization,
  123. or the operations could be added to this program in main() after
  124. memory is allocated, and the program recompiled.
  125.  
  126. If a byte is received with no errors, or it is received with errors
  127. and is correctable, then the byte is written to stdout and counted in
  128. the bytes transferred.  If the byte required correction, then it is
  129. also counted as a "fixed" byte.  The main use of the "fixed" count is
  130. to judge the quality of the link.  Fixed bytes are almost certainly
  131. good.
  132.  
  133. If a byte (or at least the illusion of start bits) is received, and
  134. the data is in error and not correctable, then nothing is written to
  135. stdout and the byte counts as an error.  If there are trailing
  136. errors, i.e. errors with no good bytes that follow them, then
  137. imbedded errors and trailing errors are counted separately.  If a
  138. transfer results in no imbedded errors, but some trailing errors,
  139. then the transfer may very well be good, since the trailing errors
  140. might just be noise received while waiting for a silence of three
  141. seconds.  For example:
  142.  
  143. 519 bytes transferred (8 fixed) with 0 imbedded errors and 1 trailing error
  144.  
  145. HPREAD has been tested and works on 6 MHz 286 systems and on 8 MHz
  146. 8088 systems.  It is not known at this time whether this program will
  147. work on 4.77 MHz 8088 systems or not.  The program utilizes the
  148. system timer which runs at the same speed on all machines (1.193182
  149. MHz), regardless of the processor's clock speed.  HPREAD should work
  150. on any system faster than those mentioned above (for example, 8 MHz
  151. 8086 systems, 16 MHz 386 systems, 1000 GHz 786 systems, etc.).
  152.  
  153. There are two options for faster machines: /f (fast) does the data
  154. decoding and error correction between bytes, and /e (enable) enables
  155. interrupts between bytes.  /f reduces the memory requirements of the
  156. program by 128K, and /e prevents the clock from losing (much) time by
  157. allowing timer tick interrupts during the transfer.  If either option
  158. is not appropriate for your machine, you will simply get errors
  159. reported for the transfer.  Even with /e, you can still lose the
  160. three seconds it takes to determine that the transmission is over.
  161.  
  162. If you have an AT compatible machine, then you can use the /c option
  163. to restore the current clock from the real time battery backed-up
  164. clock after the transfer is complete.  If /c is specified, but there
  165. is an error reading the time from the real time clock (for example,
  166. if it's an XT), then HPREAD will warn you that it did not set the
  167. current clock:
  168.  
  169.      !warning: unable read the time from the real time clock
  170.  
  171. If you have an XT compatible machine with an add-on battery backed-up
  172. clock, then that clock came with a program that sets the current
  173. clock (typically executed from AUTOEXEC.BAT).  You can write a batch
  174. file that executes HPREAD, and then executes the program to reset the
  175. current clock.
  176.  
  177. If more than 32,768 (32K) bytes are sent, then only the first 32,768
  178. bytes are written to stdout.
  179.  
  180. The command format and options can be displayed using the "/h" (help)
  181. option.  If /h is specified, no other action is performed.
  182.  
  183. When used in a batch file, HPREAD returns the following errorlevels:
  184.  
  185.    0 - No error or /h specified.
  186.    1 - Error in data, or data exceeded MAXFER (32,768) bytes.
  187.    2 - Error writing data to stdout.
  188.    3 - Could not allocate a MAXFER byte buffer, or if /f not specified,
  189.        could not allocate a 4*MAXFER byte buffer in the far heap.
  190.    4 - Invalid arguments.
  191.  
  192. This program was written to be compiled under Borland Turbo C 2.0
  193. using the small or tiny models and register variables on.
  194.  
  195. */
  196.  
  197.  
  198.  
  199. #ifndef __TURBOC__
  200. #error Need Borland Turbo C to compile HPREAD
  201. #endif
  202. #if !defined(__SMALL__) && !defined(__TINY__)
  203. #error Must use small or tiny models to compile HPREAD
  204. #endif
  205.  
  206.  
  207.  
  208. char help[] = "HPREAD 1.3  31 Jan 1990\n\
  209. Copyright (C) 1990  Mark Adler  Pasadena, CA  All rights reserved\n\
  210. \n\
  211. HPREAD reads data sent by an HP calculator to an infrared receiver\n\
  212. connected to one bit of an input port.  The data is written to\n\
  213. stdout, and statistics are sent to stderr.\n\
  214. \n\
  215. Usage:\n\
  216.    hpread               - read from printer port ERROR* line (pin 15)\n\
  217.    hpread PORT          - read from hexadecimal I/O port PORT, bit 3\n\
  218.    hpread PORT MASK     - use hexadecimal mask instead of bit 3\n\
  219. \n\
  220. Options (can be combined after a single slash):\n\
  221.    /i                   - invert the sense of the detector bit\n\
  222.    /n                   - no translation of data\n\
  223.    /c                   - restore the current clock from the real time\n\
  224.                           battery backed-up clock\n\
  225.    /e                   - enable interrupts between bytes\n\
  226.    /f                   - fast machine---do correction on the fly\n\
  227.    /h                   - show this help message\n";
  228.  
  229.  
  230.  
  231. #include <stdio.h>
  232. #include <stdlib.h>
  233. #include <alloc.h>
  234. #include <dos.h>
  235. #include <io.h>
  236. #include <fcntl.h>
  237.  
  238. #define MAXFER 32768u   /* Maximum bytes in one transfer */
  239.  
  240.  
  241.  
  242. /* Globals */
  243. unsigned fixed;         /* Count of bytes fixed by error correction */
  244. int irport;             /* The (byte) port that the IR detector is
  245.                            connected to. */
  246. char irmask;            /* Mask for the bit the IR detector is connected
  247.                            to (one of the following: 1, 2, 4, 8, 0x10,
  248.                            0x20, 0x40, or 0x80). */
  249. char irinvt;            /* 0xff if the bit is low when the detector is
  250.                            illuminated by infrared, 0 if the bit is high
  251.                            when illuminated. */
  252. char introk = 0;        /* True if interrupts allowed between bytes */
  253. char nltolf = 1;        /* True causes 0x04 to 0x0A translation on read
  254.                            and 0x0A to 0x0D,0x0A translation on write */
  255. char later = 1;         /* True defers decoding after transmission */
  256. char clock = 0;         /* True restores the current clock from the real
  257.                            time battery backed-up cmos clock */
  258.  
  259.  
  260.  
  261. long hpread(void)
  262. /* Read a byte frame from the IR port.  Interrupts should be disabled
  263.    when this routine is called.  The low 27 bits of the returned value
  264.    is set to 27 ones and zeros for the occurence or not of pulses in the
  265.    27 half-bit times.  If no pulses occur for three seconds, then zero
  266.    is returned.
  267.  
  268.    Macros used:
  269.  
  270.    IRON - expression that is true if IR is on
  271.    TIMR - used by wait to get the high byte of the tiny tick count
  272.    WAIT - wait one "tick" and increment k
  273.    LOW  - wait until IR goes off while counting ticks
  274.    HIGH - wait until IR goes on, counting ticks and rejecting noise
  275. */
  276. {
  277.   register int j, k;
  278.   int i;
  279.   long h;
  280.   unsigned char *p, t[53];
  281.  
  282.   #define IRON ((inportb(irport) ^ irinvt) & irmask)
  283.   #define TIMR {outportb(67,0);inportb(64);_AX=inportb(64);}
  284.   #define WAIT {do TIMR while(j==_AX); j=_AX; k++;}
  285.   #define LOW(d) {do{WAIT if(!IRON)break;}while(k<d);}
  286.   #define HIGH(d) {do{WAIT if(IRON){WAIT if(IRON)break;}}while(k<d);}
  287.  
  288.  
  289.   /* Wait up to three seconds for a pulse */
  290.   #define TIMEOUT 27965         /* ((14318180/12)/128)*3 */
  291.   k = j = 0;                    /* Initial j doesn't really matter */
  292.   LOW(TIMEOUT)
  293.   HIGH(TIMEOUT)
  294.   if (k >= TIMEOUT)
  295.     return 0;
  296.  
  297.  
  298.   /* Read pulses until 25 or 26 half-bit times have passed, or more than
  299.      ten half-bit times have passed with no pulses.
  300.      Mystery numbers (S=111,1=10,0=01,X=00,Y=11):
  301.      k < 11 --- for worst case SYY10, SY1X0, or S1XX0 cases, all of
  302.                 whose high+low times are ten half-bit times.
  303.      LOW(34) --- for worst case SYY1 = 8 high half-bit times,
  304.      HIGH(42) --- for worst case ten total half-bit times (see k < 11),
  305.      LOW(6) --- since last pulse should be less than one half-bit time.
  306.   */
  307.   p = t;                        /* Where to put times */
  308.   i = 0;                        /* Half-bit times so far */
  309.   do {                          /* Do loop for each high+low piece */
  310.     k = 0;                      /* Initialize tick count */
  311.     LOW(34)                     /* Wait for pulse to go low */
  312.     *p++ = k<2 ? 1 : (k+2)>>2;  /* Time high rounded to half-bits */
  313.     HIGH(42)                    /* Wait for start of next pulse */
  314.     k++;  k++;  k >>= 2;        /* Time high+low rounded */
  315.     *p++ = k;
  316.     i += k;                     /* Update total time in half-bits */
  317.   } while (k < 11 && i < 25);   /* Do until pause or enough data */
  318.   if (k < 11 && i < 27)         /* If got a final bit, then put it in */
  319.   {
  320.     *p++ = 1;
  321.     *p++ = 0;                   /* (This is fixed by next statement) */
  322.   }
  323.   p[-1] += 27-i;                /* Adjust last entry to make total=27 */
  324.   *p = 0;                       /* Terminate list */
  325.   LOW(6)                        /* Wait for last pulse to end */
  326.  
  327.  
  328.   /* Copy data into half-bit time slots */
  329.   p = t;
  330.   h = 0;
  331.   do {
  332.     j = *p++;                   /* Length of pulse in half-bit times */
  333.     k = *p++;                   /* Time from this pulse to next one */
  334.     do {
  335.       h <<= 1;
  336.       if (j)                    /* One's for pulse, zero's for quiet */
  337.       {
  338.         *((char *)&h) |= 1;     /* (Trick compiler into optimizing) */
  339.         j--;
  340.       }
  341.     } while (--k);
  342.   } while (*p);
  343.  
  344.  
  345.   /* Return the half-bit time slots (can't be zero) */
  346.   return h;
  347. }
  348.  
  349.  
  350.  
  351. int hpfix(long h)
  352. /* Do all possible error corrections on the data in h.  Return either
  353.    the corrected byte, or -1 if there were uncorrectable errors.  The
  354.    low 24 bits of h is assumed to be 24 half-bit times with ones and
  355.    zeros for pulses or not in each bit.  This is the h returned by
  356.    hpread(), but note that the three start bits are ignored.  The global
  357.    'fixed' is incremented if the byte is good, but required correction.
  358. */
  359. {
  360.   register int e, j;
  361.   int d, i;
  362.   char g;
  363.  
  364.   /* Parity bits */
  365.   #define H1 0x878
  366.   #define H2 0x4e6
  367.   #define H3 0x2d5
  368.   #define H4 0x18b
  369.  
  370.   /* Macro to get the parity of a word (zero if even, non-zero if odd.
  371.      The instructions emitted are: xor al,ah // jpo $+2 // sub ax,ax. */
  372.   #define parity(w) (_AX=w,__emit__(0x30,0xe0,0x7b,0x02,0x29,0xc0),_AX)
  373.  
  374.  
  375.   /* Convert h into a data word and an erasure word */
  376.   d = e = 0;                    /* d is the data, e the erasures */
  377.   for (j = 1; j < 4096; j<<=1)  /* Examine the 12 full-bit times */
  378.   {
  379.     g = (char)h;
  380.     h >>= 1;
  381.     if (((char)h ^ g) & 1)      /* If it is 10 or 01, */
  382.     {                           /*  then it's a good bit (10 is 1), */
  383.       if ((char)h & 1)
  384.         d |= j;
  385.     }
  386.     else
  387.       e |= j;                   /*  otherwise, it is an erasure. */
  388.     h >>= 1;
  389.   }
  390.  
  391.  
  392.   /* Correct as many errors as possible */
  393.   g = e != 0;                   /* Remember if there were errors */
  394.   while (e)
  395.   {
  396.     i = e;                      /* Set i to the erasures to be fixed */
  397.     do {
  398.       j = ((i-1) & i) ^ i;      /* Make j the first one in i */
  399.       if (((H1 ^ j) & e) == 0)  /* If we can, fix j using H1 */
  400.         {if (parity(H1 & d)) d |= j; e ^= j; break;}
  401.       if (((H2 ^ j) & e) == 0)  /* If we can, fix j using H2 */
  402.         {if (parity(H2 & d)) d |= j; e ^= j; break;}
  403.       if (((H3 ^ j) & e) == 0)  /* If we can, fix j using H3 */
  404.         {if (parity(H3 & d)) d |= j; e ^= j; break;}
  405.       if (((H4 ^ j) & e) == 0)  /* If we can, fix j using H4 */
  406.         {if (parity(H4 & d)) d |= j; e ^= j; break;}
  407.       i ^= j;                   /* None worked, try next bit */
  408.     } while (i);
  409.     if (i == 0)                 /* If no bits corrected, give up */
  410.       break;
  411.   }
  412.  
  413.  
  414.   /* If there are still erasures or parity errors, then return error */
  415.   if (e || parity(H1 & d) || parity(H2 & d) || parity(H3 & d) ||
  416.       parity(H4 & d))
  417.     return -1;
  418.  
  419.  
  420.   /* Got a good byte---return it */
  421.   if (g)
  422.     fixed++;                    /* If had errors, update fixed */
  423.   return d & 0xff;              /* Strip parity bits */
  424. }
  425.  
  426.  
  427.  
  428. void err(int n, char *m)
  429. /* Exit with return code n after printing message m to stderr */
  430. {
  431.   fputs(m, stderr);
  432.   exit(n);
  433. }
  434.  
  435.  
  436.  
  437. void main(int argc, char *argv[])
  438. /* Read a stream of bytes from the HP and write them to stdout.  See
  439.    comments above for the command format. */
  440. {
  441.   register int r;
  442.   register unsigned i;
  443.   unsigned j, e, t;
  444.   long h;
  445.   char *b;
  446.   long huge *d, huge *p;
  447.   union REGS g;
  448.  
  449.  
  450.   /* Initialize irport to the first printer's status port, set irmask to
  451.      the ERROR* line (pin 15 on the DB-25), and the bit is input as
  452.      presented, so it must be inverted since the detector output is
  453.      active low. */
  454.   irport = *((int far *)MK_FP(0, 0x408)) + 1;
  455.   irmask = 8;
  456.   irinvt = 0xff;
  457.  
  458.  
  459.   /* Process arguments */
  460.   j = 0;                        /* Count of non-option arguments */
  461.   for (i = 1; i < argc; i++)
  462.     if (argv[i][0] != '/')      /* Non-option */
  463.       if (j == 0)               /* First non-option arg */
  464.         if (sscanf(argv[i], "%x", &irport) != 1)
  465.           err(4, "?invalid argument---use HPREAD /H for help\n");
  466.         else
  467.           j++;
  468.       else if (j == 1)          /* Second non-option arg */
  469.         if (sscanf(argv[i], "%x", &e) != 1)
  470.           err(4, "?invalid argument---use HPREAD /H for help\n");
  471.         else
  472.         {
  473.           irmask = (char) e;
  474.           j++;
  475.         }
  476.       else                      /* Third non-option arg */
  477.         err(4, "?invalid argument---use HPREAD /H for help\n");
  478.     else                        /* Option(s) */
  479.       for (r = 1; argv[i][r]; r++)
  480.         switch (argv[i][r] & 0x5f)
  481.         {
  482.         case 'C': clock = 1; break;
  483.         case 'E': introk = 1; break;
  484.         case 'F': later = 0; break;
  485.         case 'H': err(0, help); break;
  486.         case 'I': irinvt = 0; break;
  487.         case 'N': nltolf = 0; break;
  488.         default:
  489.           err(4, "?invalid argument---use HPREAD /H for help\n");
  490.         }
  491.         
  492.  
  493.   /* Allocate memory for data */
  494.   if ((b = malloc(MAXFER)) == NULL ||
  495.       (later && (p = d = farcalloc(MAXFER, sizeof(long))) == NULL))
  496.     err(3, "!not enough memory---aborting HPREAD\n");
  497.  
  498.  
  499.   /* Read bytes until timeout or MAXFER bytes exceeded */
  500.   t = e = i = 0;
  501.   while (i < MAXFER)
  502.   {
  503.     disable();                  /* Disable interrupts during hpread() */
  504.     if ((h = hpread()) == 0)    /* Read a frame---if timeout, */
  505.       break;                    /*  then done. */
  506.     if (introk)                 /* If allowed, enable interrupts */
  507.       enable();                 /*  between bytes. */
  508.     if (later)                  /* If decoding deferred, */
  509.     {                           /*  just save the half-bit pulses */
  510.       *p++ = h;
  511.       i++;
  512.     }
  513.     else                        /*  else, do the work between bytes */
  514.       if ((r = hpfix(h)) == -1) /* Try to correct errors, if any */
  515.         t++;                    /* If failure, skip the byte and */
  516.       else                      /*  update the trailing error count. */
  517.       {
  518.         b[i++] = nltolf && r==4 ? 10 : r;       /* Save good byte */
  519.         e += t;                 /* Update the main error count and */
  520.         t = 0;                  /*  reset the trailing error count */
  521.       }
  522.   }
  523.   enable();                     /* Re-enable interrupts */
  524.  
  525.  
  526.   /* Restore the timer count from the real-time cmos clock (if any).
  527.      Uses the 0x1a interrupt with AH=2 to get the cmos clock time.  If
  528.      the machine has an XT compatible BIOS (and therefore, no AT
  529.      compatible clock), then AH will come back as 1.  If the machine is
  530.      AT compatible, then AH will come back as 0 and carry will be either
  531.      clear if it got the time, or set if there was an error reading the
  532.      clock.  It is necessary to use emitted code here to control the
  533.      carry flag before doing the interrupt. */
  534.   if (clock)
  535.   {
  536.     _AH = 2;
  537.     /* Code emitted is: clc, int 1ah, sbb al,al */
  538.     __emit__(0xf8,0xcd,0x1a,0x18,0xc0);
  539.     if (_AX == 0)
  540.     {
  541.       /* Compute the new timer value (18.207 ticks = 1 second) */
  542.       j = _CX;
  543.       r = _DH;
  544.       #define bcd(a) (((a) & 0x0f) + 10*(((a) >> 4) & 0x0f))
  545.       h = ((bcd(r) + 60L*(bcd(j) + 60L*bcd(j >> 8)))*18207+500)/1000;
  546.  
  547.       /* Set the new timer value */
  548.       g.x.dx = (unsigned int) h;
  549.       g.x.cx = (unsigned int) (h >> 16);
  550.       g.h.ah = 1;
  551.       int86(0x1a, &g, &g);
  552.     }
  553.     else
  554.       fputs("!warning: unable read the time from the real time clock\n",
  555.             stderr);
  556.   }
  557.  
  558.  
  559.   /* If deferred, then do the decoding and error correction */
  560.   if (later)
  561.   {
  562.     p = d;
  563.     j = 0;
  564.     for (; i; i--)
  565.       if ((r = hpfix(*p++)) == -1)      /* Try to correct errors */
  566.         t++;                    /* If failure, skip the byte and */
  567.       else                      /*  update the trailing error count. */
  568.       {
  569.         b[j++] = nltolf && r==4 ? 10 : r;       /* Save good byte */
  570.         e += t;                 /* Update the main error count and */
  571.         t = 0;                  /*  reset the trailing error count */
  572.       }
  573.     i = j;
  574.   }
  575.  
  576.  
  577.   /* Summarize activity */
  578.   fprintf(stderr,
  579.     "%u byte%s transferred (%u fixed) with %u %serror%s",
  580.     i, i==1 ? "" : "s", fixed,
  581.     e, t ? "imbedded " : "", e==1 ? "" : "s");
  582.   if (t)
  583.     fprintf(stderr, " and %u trailing error%s\n",
  584.             t, t==1 ? "" : "s");
  585.   putc('\n', stderr);
  586.  
  587.  
  588.   /* Write data to stdout */
  589.   if (!nltolf)                  /* If no translation, write in binary */
  590.     setmode(stdout->fd, O_BINARY);
  591.   if (fwrite(b, i, 1, stdout) != 1)
  592.     err(2, "?error writing transferred data to stdout\n");
  593.   if (e || i == MAXFER)
  594.     exit(1);
  595.   else
  596.     exit(0);
  597. }
  598.