home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / hamradio / cw.arc / CW.C next >
Text File  |  1988-10-22  |  39KB  |  1,294 lines

  1. static char *notice =
  2.  
  3. "Copyright 1988 by Kevin Schmidt W9CF \
  4.  You may copy and distribute this program for noncommercial use.\
  5.  You may modify this program if this notice is left \
  6.  intact. Compiled object or executable files made from this or a \
  7.  modified version may only be distributed if accompanied \
  8.  by their source code. \
  9.  DISCLAIMER: \
  10.  Since this code plays around with the innards of the pc and I am \
  11.  not an expert on pc programming, you run this program at your own risk.";
  12.  
  13. /*
  14.  This program sends and receives morse code using
  15.  the pc serial port.
  16.  
  17.  The code is written using TURBO C (R) Borland International.
  18.  It is coded for key down = +12v and key up = -12v.
  19.  The clear to send line is used for input and request to send for output.
  20.  It uses com1 on pc-at by default. For other ports or machines
  21.  change the serial port address and interrupt number.
  22.  The code has been tested on my 6MHZ ibm pc-at running dos 3.1
  23.  with CGA and serial/parallel adaptor and on a zenith z-181 portable.
  24.  Any other 80 column or greater display should work. Since the clock
  25.  chip is reprogrammed and serial port interrupts are used,
  26.  probably only hardware as well as bios level clones of the IBM pc will
  27.  run this code. 
  28.  
  29.  The program can be divided into three parts. The first part is an interrupt
  30.  handler for the serial port. The serial port is set to interrupt on
  31.  change of the modem status. In particular, this happens when the clear
  32.  to send line is changed. The interrupt handler simply records the
  33.  status of the clear to send line in queue.mark and the time since
  34.  the clear to send line last changed in queue.mstime and increments
  35.  the queue pointer countin. The serial port stuff is fairly standard
  36.  and I used the description given by of R. Jourdain,
  37.  "Programmer's Problem Solver for the IBM PC,XT, and AT",
  38.  (Prentice Hall, 1986, NY). The method used for timing the marks 
  39.  and spaces is adapted from B. Sheppard, "High Performance Software
  40.  Analysis on the IBM PC", BYTE, January 1987.
  41.  
  42.  The second part of the code is a replacement for the IRQ0 timer
  43.  interrupt. I change the timer to interrupt SPEEDUP times faster
  44.  than original. Every SPEEDUP interrupts the old interrupt handler is
  45.  called so that disk operations and the bios date should be unaffected.
  46.  The replacement interrupt handler takes characters from a queue
  47.  and keys the request to send line of the serial port. Note if
  48.  you only want to receive morse this replacement routine is not
  49.  necessary since microsecond timing accuracy can be obtained without
  50.  changing this interrupt just change SPEEDUP to 1. Note SPEEDUP
  51.  must be a power of 2.
  52.  
  53.  The last part of the code is the human interface, which looks at
  54.  the receive queue and displays the result along with some statistics,
  55.  and also fills the transmit buffer.
  56.  
  57.  Since both the receive and transmit pieces are interrupt driven, the
  58.  code can be tested by shorting the clear to send line to the request to
  59.  send line. This will loop back the sent morse to the morse decoder.
  60.  
  61. */
  62. #include <stdio.h>
  63. #include <dos.h>
  64. #include <bios.h>
  65. #define max(A,B)  ((A) > (B) ? (A) : (B))
  66. #define min(A,B)  ((A) < (B) ? (A) : (B))
  67. #define MARK 0
  68. #define SPACE 1
  69. #define QSIZE 1024
  70. #define SAMPLE 64
  71. #define PRINTTIME 2000
  72. #define SPEEDUP 16    /* must be > 0 and a power of 2 */
  73. #define SQSIZE 4096
  74. #define BSIZE 150
  75. /*
  76.  video stuff
  77. */
  78. static unsigned printtick = 0;
  79. static struct video { unsigned char mode,page,width; int csize; } video;
  80. static struct cpos { int x,y; } wxmt,wrcv,wbuf;
  81. /*
  82.  define queue of times and whether mark or space
  83. */
  84. static struct queue {
  85.     long mstime[QSIZE];
  86.     char mark[QSIZE];
  87.     int countin,countout; } queue;
  88. /*
  89.  stuff to read time between interrupts
  90. */
  91. static struct timer {
  92.     long tick1,tick2,fast1,fast2; } timer;
  93. static struct sound { int on,frequency; unsigned char low,high; } sound;
  94. /*
  95.  serial port address and vector -- these values are for com1 on PC-AT
  96.  they must be changed to use another serial port or on another machine
  97.  this can be done here or in cw.ini initialization file
  98. */
  99. static struct serial { unsigned int address,vector; } serial = { 0x3f8, 4 };
  100. static int autospeed = 1;
  101. /*
  102.  following are tables of morse characters. c1 is table of characters of
  103.  length 1, c2 of length 2, etc.
  104.  think in binary. dot = 0, dash = 1 so e.g. c = dash dot dash dot 
  105.  is entry number ten (eleventh starting from zero) for the characters
  106.  of length 4.
  107. */
  108. static char *c0[1] = { "" };
  109. static char *c1[2] = {"e","t"};
  110. static char *c2[4] = {"i","a","n","m"};
  111. static char *c3[8] = {"s","u","r","w","d","k","g","o"};
  112. static char *c4[16] = {"h","v","f","X","l","X","p","j"
  113.    ,"b","x","c","y","z","q","X","X"};
  114. static char *c5[32] = {"5","4"," UNDERSTOOD ","3","X","X","X","2"
  115.    ," WAIT ","X"," END OF MESSAGE ","X","X","X","X","1"
  116.    ,"6","=","/","X","X"," START ","(","X"
  117.    ,"7","X","X","X","8","X","9","0"};
  118. static char *c6[64] = {" ERROR ","X","X","X","X"," 30 ","X","X"
  119.    ,"X","$","X","X","?","_","X","X"
  120.    ,"X","X","\"","X"," PARAGRAPH ",".","X","X"
  121.    ,"X","X","X","X","X","X","X","X"
  122.    ,"X","-","X","X","X","X","X","X"
  123.    ,"X","X",";","X","X",")","X","X"
  124.    ,"X","X","X",",","X","X","X","X"
  125.    ,":","X","X","X","X","X","X","X"};
  126. static char **c[7] = { c0,c1,c2,c3,c4,c5,c6 };
  127. /*
  128.  new and old interrupt vector for serial port
  129.  and bios hardware timer interrupt
  130. */
  131. static void interrupt comvec();
  132. static void interrupt (*oldcomvec)();
  133. static void interrupt htimevec();
  134. static void interrupt (*oldhtimevec)();
  135. static long newtick = 0;
  136. static unsigned char b1[BSIZE],b2[BSIZE],b3[BSIZE],b4[BSIZE],b5[BSIZE];
  137. static unsigned char b6[BSIZE],b7[BSIZE],b8[BSIZE],b9[BSIZE],b10[BSIZE];
  138. static unsigned char *b[10] = { b1,b2,b3,b4,b5,b6,b7,b8,b9,b10 };
  139. static int bufnow = 0;
  140. static struct send { unsigned int clock,alarm;
  141.    unsigned char queue[SQSIZE];
  142.    unsigned int in,out;
  143.    int on; } send;
  144. static struct sletter { long element; int length; } sletter[128] = {
  145.         0L, 0,        21845L,15,            0L, 0,            0L, 0,
  146.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  147.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  148.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  149.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  150.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  151.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  152.         0L, 0,            0L, 0,            0L, 0,            0L, 0,
  153.         0L, 4,            0L, 0,        23901L,15,            0L, 0,
  154.    120277L,17,            0L, 0,            0L, 0,       122333L,19,
  155.     24023L,15,       482775L,19,            0L, 0,         5981L,13,
  156.    488823L,19,        30039L,15,       120669L,17,         5975L,13,
  157.    489335L,19,       122333L,17,        30581L,15,         7637L,13,
  158.      1877L,11,          341L, 9,         1367L,11,         5495L,13,
  159.     22391L,15,        96119L,17,        87927L,17,        95703L,17,
  160.         0L, 0,         7511L,13,            0L, 0,        22389L,15,
  161.        0L, 0,           29L, 5,          343L, 9,         1495L,11,
  162.        87L, 7,            1L, 1,          373L, 9,          375L, 9,
  163.        85L, 7,            5L, 3,         7645L,13,          471L, 9,
  164.       349L, 9,          119L, 7,           23L, 5,         1911L,11,
  165.      1501L,11,         7543L,13,           93L, 7,           21L, 5,
  166.         7L, 3,          117L, 7,          469L, 9,          477L, 9,
  167.      1879L,11,         7639L,13,         1399L,11,            0L, 0,
  168.         0L, 0,            0L, 0,            0L, 0,       120693L,17,
  169.         0L, 0,           29L, 5,          343L, 9,         1495L,11,
  170.        87L, 7,            1L, 1,          373L, 9,          375L, 9,
  171.        85L, 7,            5L, 3,         7645L,13,          471L, 9,
  172.       349L, 9,          119L, 7,           23L, 5,         1911L,11,
  173.      1501L,11,         7543L,13,           93L, 7,           21L, 5,
  174.         7L, 3,          117L, 7,          469L, 9,          477L, 9,
  175.      1879L,11,         7639L,13,         1399L,11,            0L, 0,
  176.         0L, 0,            0L, 0,            0L, 0,            0L, 0 };
  177. struct stats { long sample[SAMPLE]; int count; double sum; };
  178. static struct stats mark,space,dot,dash,elsp,letsp;
  179. static int change = 8;
  180. static int done = 0;
  181. static long dot2,noise;
  182. static long stretch = 0L;
  183. main()
  184. {
  185.    int i,j,ii;
  186.    unsigned int letter = 0,size = 0;
  187.    void init(),end(),clear(),rcv_print(),info(),getkey(),cursor(),help();
  188.    void reader();
  189. /*
  190.  set sound on and frequency at 800HZ then initialize sound and
  191.  interrupts
  192. */
  193.    sound.frequency = 800;
  194.    sound.on = 1;
  195.    sound.low = (1193280L/sound.frequency)%256;
  196.    sound.high = (1193280L/sound.frequency)/256;
  197.    send.clock = 0;
  198.    send.alarm = (100*SPEEDUP)/64;
  199.    send.on = 0;
  200.    send.in = send.out = 0;
  201.    init();
  202. /*
  203.  set speed value for about 15wpm this should be ok for about
  204.  1/2 to twice this speed, and with luck will correct itself
  205.  for other speeds
  206. */
  207.    dot2 = 200000L;
  208.    noise = dot2/40L;
  209. /*
  210.  get video mode stuff, clear screen, and set cursor position
  211. */
  212.    clear();
  213.    help();
  214. /*
  215.  read initialization file to change any of above stuff
  216. */
  217.    reader();
  218. /*
  219.  fill tables for statistics
  220. */
  221.    for (i=0;i<SAMPLE;i++) {
  222.       mark.sample[i] = dot2;
  223.       dot.sample[i] = elsp.sample[i] = dot2/2;
  224.       dash.sample[i] = letsp.sample[i] = 3*dot2/2;
  225.    }
  226.    mark.sum = SAMPLE*dot2;
  227.    dot.sum = elsp.sum = SAMPLE*dot2/2;
  228.    dash.sum = letsp.sum = SAMPLE*3*dot2/2;
  229.    mark.count = dot.count = dash.count = elsp.count = letsp.count = 0;
  230. /*
  231.  loop until told to quit
  232. */
  233.    while(!done) {
  234. /*
  235.  if key has been pressed, do something.
  236. */
  237.       if (bioskey(1)) {
  238.          getkey();
  239.       }
  240. /*
  241.  update statistics printout if they have had a chance to change
  242. */
  243.       if (change >= 8) {
  244.          info(dot.sum,dash.sum,elsp.sum,letsp.sum,dot2);
  245.          change = 0;
  246.       }
  247. /*
  248.  if no input for PRINTTIME clock ticks then print out last letter
  249. */
  250.       if (size != 0 && printtick > PRINTTIME) {
  251.          size = min(size,6);
  252.          letter &= 0x3f;
  253.          rcv_print(c[size][letter]);
  254.          rcv_print(" ");
  255.          letter = 0;
  256.          size = 0;
  257.          printtick = 0;
  258.       }
  259. /*
  260.  start real stuff here - find out if some code has come in
  261. */
  262.       while(queue.countout != queue.countin) {
  263.          printtick = 0;
  264. /*
  265.  ignore if it looks like noise from contact bounce
  266. */
  267.          if (queue.mstime[queue.countout] > noise) {
  268. /*
  269.  check if mark or space and act accordingly
  270. */
  271.             switch (queue.mark[queue.countout]) {
  272.             case MARK:
  273.                queue.mstime[queue.countout] -= stretch;
  274.                size++;
  275.                if (autospeed) {
  276.                   mark.sum -= mark.sample[mark.count];
  277.                   mark.sum +=
  278.                    (mark.sample[mark.count++] = queue.mstime[queue.countout]);
  279.                   mark.count %= SAMPLE;
  280. /*
  281.  set speed to be given by 2 dot times = average of last SAMPLE marks
  282.  this method should be replaced with a better statistical analysis
  283.  of both marks and spaces
  284. */
  285.                   dot2 = (mark.sum)/(SAMPLE);
  286.                   noise = dot2/40L+stretch;
  287.                }
  288.                change++;
  289. /*
  290.  new mark so shift letter left 1 and fill rightmost bit with 0 for dot
  291.  and 1 for dash
  292. */
  293.                letter *= 2;
  294.                if (queue.mstime[queue.countout] > dot2) {
  295.                   letter |= 1;
  296.                   dash.sum -= dash.sample[dash.count];
  297.                   dash.sum += (dash.sample[dash.count++] = 
  298.                      queue.mstime[queue.countout]);
  299.                   dash.count %= SAMPLE;
  300.                } else {
  301.                   dot.sum -= dot.sample[dot.count];
  302.                   dot.sum += (dot.sample[dot.count++] =
  303.                      queue.mstime[queue.countout]);
  304.                   dot.count %= SAMPLE;
  305.                }
  306.                break;
  307. /*
  308.  new space so find out if it is the end of a character element, letter or word
  309.  and act accordingly
  310. */
  311.             case SPACE:
  312.                queue.mstime[queue.countout] += stretch;
  313.                if (queue.mstime[queue.countout] > dot2) {
  314.                   size = min(size,6);
  315.                   letter &= 0x3f;
  316.                   if (queue.mstime[queue.countout] > 5L*dot2/2L) {
  317.                      rcv_print(c[size][letter]);
  318.                      rcv_print(" ");
  319.                   } else {
  320.                      rcv_print(c[size][letter]);
  321.                      letsp.sum -= letsp.sample[letsp.count];
  322.                      letsp.sum += (letsp.sample[letsp.count++] =
  323.                         queue.mstime[queue.countout]);
  324.                      letsp.count %= SAMPLE;
  325.                   }
  326.                   letter = 0;
  327.                   size = 0;
  328.                } else {
  329.                   elsp.sum -= elsp.sample[elsp.count];
  330.                   elsp.sum += (elsp.sample[elsp.count++] =
  331.                      queue.mstime[queue.countout]);
  332.                   elsp.count %= SAMPLE;
  333.                }
  334.                break;
  335.             }
  336.          }
  337. /*
  338.  queue serviced so update pointer
  339. */
  340.          queue.countout++;
  341.          queue.countout %= QSIZE;
  342.       }
  343.    }
  344.    end();
  345. }
  346. void getkey()
  347. {
  348. /*
  349.  keyboard action -- find out why
  350. */
  351.    int j,i,cc,ccc;
  352.    unsigned char new;
  353.    char s[80];
  354.    static int point = 0;
  355.    void xmt_print(),cursor(),buf_clear(),buf_print(),analyze();
  356.    cc = bioskey(0);
  357.    ccc = cc & 0x007f;
  358. /*
  359.  Check to see if it is an ascii character. If so add to
  360.  send queue and exit
  361. */
  362.    if (ccc > 0 && ccc < 128) { 
  363. /*
  364.  check for delete key -- try to take back character this could be done
  365.  better.
  366. */
  367.       if (bufnow == 0) {
  368.          if (ccc == 0x8) {               /* delete key */
  369.             if (send.on) {
  370.                send.on = 0;
  371.                if (send.in != send.out) {
  372.                   send.in = (send.in+SQSIZE-1) % SQSIZE;
  373.                   if (wxmt.x == 0) {
  374.                      if (wxmt.y != 17) {
  375.                         wxmt.x = video.width-1;
  376.                         wxmt.y -= 1;
  377.                      }
  378.                   } else {
  379.                      wxmt.x -= 1;
  380.                   }
  381.                   cursor(wxmt.x,wxmt.y);
  382.                }
  383.                send.on = 1;
  384.             } else {
  385.                if (send.in != send.out) {
  386.                   send.in = (send.in+SQSIZE-1) % SQSIZE;
  387.                   if (wxmt.x == 0) {
  388.                      wxmt.x = video.width-1;
  389.                      wxmt.y -= 1;
  390.                   } else {
  391.                      wxmt.x -= 1;
  392.                   }
  393.                   cursor(wxmt.x,wxmt.y);
  394.                }
  395.             }
  396.          } else {                        /* other ascii just send*/
  397.             if (ccc > 31) {
  398.                j = (send.in+1) % SQSIZE;
  399.                send.queue[j] = (unsigned char)ccc;
  400.                send.in = j;
  401.                xmt_print((char) ccc);
  402.             }
  403.          }
  404.       } else {
  405.          switch(ccc) {
  406.          case 0x8:
  407.             if (wbuf.x == 0) {
  408.                if (wbuf.y != 23) {
  409.                   wbuf.x = video.width-1;
  410.                   wbuf.y -= 1;
  411.                }
  412.             } else {
  413.                wbuf.x -= 1;
  414.             }
  415.             cursor(wbuf.x,wbuf.y);
  416.             point--;
  417.             point = max(point,0);
  418.             break;
  419.          case 0x1b:
  420.          case 0x1a:
  421.          case 0x04:
  422.          case 0x0d:
  423.             point = min(point,BSIZE);
  424.             b[bufnow-1][point++] = '\0';
  425.             point = 0;
  426.             bufnow = 0;
  427.             break;
  428.          default:
  429.             point = min(point,BSIZE);
  430.             b[bufnow-1][point++] = (unsigned char) ccc;
  431.             buf_print((unsigned char) ccc);
  432.          }
  433.       }
  434.    } else {
  435. /*
  436.  else see what we need to do
  437. */
  438.       if (bufnow != 0) {
  439.          b[bufnow-1][point++] = '\0';
  440.          point = 0;
  441.          bufnow = 0;
  442.       }
  443.       switch (cc) {
  444.       case 0x4800: /* up arror raise tone */
  445.          sound.frequency += 10;
  446.          sound.low = (1193280L/sound.frequency)%256;
  447.          sound.high = (1193280L/sound.frequency)/256;
  448.          outportb(0x43,0xb6);
  449.          outportb(0x42,sound.low);
  450.          outportb(0x42,sound.high);
  451.          break;
  452.       case 0x5000: /* down arrow lower tone*/
  453.          sound.frequency -= 10;
  454.          sound.low = (1193280L/sound.frequency)%256;
  455.          sound.high = (1193280L/sound.frequency)/256;
  456.          outportb(0x43,0xb6);
  457.          outportb(0x42,sound.low);
  458.          outportb(0x42,sound.high);
  459.          break;
  460.       case 0x3b00: /* f1 toggle sound*/
  461.          if (sound.on) {
  462.             sound.on = 0;
  463.             new = inportb(0x61);
  464.             new &= 0xfc;
  465.             outportb(0x61,new);
  466.          } else {
  467.             sound.on = 1;
  468.             outportb(0x43,0xb6);
  469.             outportb(0x42,sound.low);
  470.             outportb(0x42,sound.high);
  471.          }
  472.          break;
  473.       case 0x3c00: /* f2 toggle autospeed on/off */
  474.          if (autospeed) autospeed = 0; else autospeed = 1;
  475.          change = 8;
  476.          break;
  477.       case 0x3d00: /* f3 if manual speed increase it*/
  478.          if (autospeed == 0) {
  479.             dot2 = (dot2*10)/9;
  480.             for (i=0;i<SAMPLE;i++) {
  481.                mark.sample[i] = dot2;
  482.             }
  483.             mark.sum = SAMPLE*dot2;
  484.             noise = dot2/40L+stretch;
  485.             change = 8;
  486.          }
  487.          break;
  488.       case 0x3e00: /* f4 if manual speed decrease it*/
  489.          if (autospeed == 0) {
  490.             dot2 = (dot2*9)/10;
  491.             dot2 = max(10000L,dot2);
  492.             for (i=0;i<SAMPLE;i++) {
  493.                mark.sample[i] = dot2;
  494.             }
  495.             mark.sum = SAMPLE*dot2;
  496.             noise = dot2/40L+stretch;
  497.             change = 8;
  498.          }
  499.          break;
  500.       case 0x4400: /* f10 done */
  501.          done = 1;
  502.          break;
  503.       case 0x3f00: /* f5 decrease sending speed*/
  504.          i = send.alarm;
  505.          send.alarm *= 11;
  506.          send.alarm /= 10;
  507.          if (send.alarm == i) send.alarm++;
  508.          change = 8;
  509.          break;
  510.       case 0x4000: /* f6 increase sending speed*/
  511.          send.alarm = max((9*send.alarm)/10,1);
  512.          change = 8;
  513.          break;
  514.       case 0x4100: /* f7 set sending speed to receive speed */
  515.          send.alarm = max((dot2*SPEEDUP)/131078L,1L);
  516.          change = 8;
  517.          break;
  518.       case 0x4200: /* f8 command */
  519.          buf_clear();
  520.          buf_print('C'); buf_print('o'); buf_print('m');
  521.          buf_print('m'); buf_print('a'); buf_print('n');
  522.          buf_print('d'); buf_print(':'); buf_print(' ');
  523.          for(i=0;((ccc = bioskey(0)) & 0x7f) != '\015';) {
  524.             ccc &= 0x7f;
  525.             if (ccc == 0x8) {
  526.                if (i > 0) {
  527.                   i--;
  528.                   if (wbuf.x == 0) {
  529.                      wbuf.x = video.width-1;
  530.                      wbuf.y -= 1;
  531.                   } else {
  532.                      wbuf.x -= 1;
  533.                   }
  534.                   cursor(wbuf.x,wbuf.y);
  535.                
  536.                }
  537.             } else {
  538.                s[i++] = (char) ccc;
  539.                buf_print(ccc);
  540.             }
  541.          }
  542.          s[i] = '\0';
  543.          analyze(s);
  544.          break;
  545.       case 0x4300: /* f9 toggle sending on/off */
  546.         send.on = 1-send.on;
  547.         break;
  548.       case 0x5400: /* shift f1 */
  549.          for (i=0;i<BSIZE && b1[i] != '\0';i++)  {
  550.             j = (send.in+1) % SQSIZE;
  551.             send.queue[j] = b1[i];
  552.             send.in = j;
  553.             xmt_print(b1[i]);
  554.          }
  555.          break;
  556.       case 0x5500: /* shift f2 */
  557.          for (i=0;i<BSIZE && b2[i] != '\0';i++)  {
  558.             j = (send.in+1) % SQSIZE;
  559.             send.queue[j] = b2[i];
  560.             send.in = j;
  561.             xmt_print(b2[i]);
  562.          }
  563.          break;
  564.       case 0x5600: /* shift f3 */
  565.          for (i=0;i<BSIZE && b3[i] != '\0';i++)  {
  566.             j = (send.in+1) % SQSIZE;
  567.             send.queue[j] = b3[i];
  568.             send.in = j;
  569.             xmt_print(b3[i]);
  570.          }
  571.          break;
  572.       case 0x5700: /* shift f4 */
  573.          for (i=0;i<BSIZE && b4[i] != '\0';i++)  {
  574.             j = (send.in+1) % SQSIZE;
  575.             send.queue[j] = b4[i];
  576.             send.in = j;
  577.             xmt_print(b4[i]);
  578.          }
  579.          break;
  580.       case 0x5800: /* shift f5 */
  581.          for (i=0;i<BSIZE && b5[i] != '\0';i++)  {
  582.             j = (send.in+1) % SQSIZE;
  583.             send.queue[j] = b5[i];
  584.             send.in = j;
  585.             xmt_print(b5[i]);
  586.          }
  587.          break;
  588.       case 0x5900: /* shift f6 */
  589.          for (i=0;i<BSIZE && b6[i] != '\0';i++)  {
  590.             j = (send.in+1) % SQSIZE;
  591.             send.queue[j] = b6[i];
  592.             send.in = j;
  593.             xmt_print(b6[i]);
  594.          }
  595.          break;
  596.       case 0x5a00: /* shift f7 */
  597.          for (i=0;i<BSIZE && b7[i] != '\0';i++)  {
  598.             j = (send.in+1) % SQSIZE;
  599.             send.queue[j] = b7[i];
  600.             send.in = j;
  601.             xmt_print(b7[i]);
  602.          }
  603.          break;
  604.       case 0x5b00: /* shift f8 */
  605.          for (i=0;i<BSIZE && b8[i] != '\0';i++)  {
  606.             j = (send.in+1) % SQSIZE;
  607.             send.queue[j] = b8[i];
  608.             send.in = j;
  609.             xmt_print(b8[i]);
  610.          }
  611.          break;
  612.       case 0x5c00: /* shift f9 */
  613.          for (i=0;i<BSIZE && b9[i] != '\0';i++)  {
  614.             j = (send.in+1) % SQSIZE;
  615.             send.queue[j] = b9[i];
  616.             send.in = j;
  617.             xmt_print(b9[i]);
  618.          }
  619.          break;
  620.       case 0x5d00: /* shift f10 */
  621.          for (i=0;i<BSIZE && b10[i] != '\0';i++)  {
  622.             j = (send.in+1) % SQSIZE;
  623.             send.queue[j] = b10[i];
  624.             send.in = j;
  625.             xmt_print(b10[i]);
  626.          }
  627.          break;
  628.       case 0x5e00: /* ctrl f1 */
  629.          buf_clear();
  630.          buf_print('B'); buf_print('1'); buf_print(':');
  631.          bufnow = 1;
  632.          break;
  633.       case 0x5f00: /* ctrl f2 */
  634.          buf_clear();
  635.          buf_print('B'); buf_print('2'); buf_print(':');
  636.          bufnow = 2;
  637.          break;
  638.       case 0x6000: /* ctrl f3 */
  639.          buf_clear();
  640.          buf_print('B'); buf_print('3'); buf_print(':');
  641.          bufnow = 3;
  642.          break;
  643.       case 0x6100: /* ctrl f4 */
  644.          buf_clear();
  645.          buf_print('B'); buf_print('4'); buf_print(':');
  646.          bufnow = 4;
  647.          break;
  648.       case 0x6200: /* ctrl f5 */
  649.          buf_clear();
  650.          buf_print('B'); buf_print('5'); buf_print(':');
  651.          bufnow = 5;
  652.          break;
  653.       case 0x6300: /* ctrl f6 */
  654.          buf_clear();
  655.          buf_print('B'); buf_print('6'); buf_print(':');
  656.          bufnow = 6;
  657.          break;
  658.       case 0x6400: /* ctrl f7 */
  659.          buf_clear();
  660.          buf_print('B'); buf_print('7'); buf_print(':');
  661.          bufnow = 7;
  662.          break;
  663.       case 0x6500: /* ctrl f8 */
  664.          buf_clear();
  665.          buf_print('B'); buf_print('8'); buf_print(':');
  666.          bufnow = 8;
  667.          break;
  668.       case 0x6600: /* ctrl f9 */
  669.          buf_clear();
  670.          buf_print('B'); buf_print('9'); buf_print(':');
  671.          bufnow = 9;
  672.          break;
  673.       case 0x6700: /* ctrl f10 */
  674.          buf_clear();
  675.          buf_print('B'); buf_print('1'); buf_print('0'); buf_print(':');
  676.          bufnow = 10;
  677.          break;
  678.       case 0x6800: /* alt f1 */
  679.          buf_clear();
  680.          buf_print('B'); buf_print('1'); buf_print(':');
  681.          for(i=0;i<BSIZE && b[0][i] != 0;i++) buf_print(b[0][i]);
  682.          break;
  683.       case 0x6900: /* alt f2 */
  684.          buf_clear();
  685.          buf_print('B'); buf_print('2'); buf_print(':');
  686.          for(i=0;i<BSIZE && b[1][i] != 0;i++) buf_print(b[1][i]);
  687.          break;
  688.       case 0x6a00: /* alt f3 */
  689.          buf_clear();
  690.          buf_print('B'); buf_print('3'); buf_print(':');
  691.          for(i=0;i<BSIZE && b[2][i] != 0;i++) buf_print(b[2][i]);
  692.          break;
  693.       case 0x6b00: /* alt f4 */
  694.          buf_clear();
  695.          buf_print('B'); buf_print('4'); buf_print(':');
  696.          for(i=0;i<BSIZE && b[3][i] != 0;i++) buf_print(b[3][i]);
  697.          break;
  698.       case 0x6c00: /* alt f5 */
  699.          buf_clear();
  700.          buf_print('B'); buf_print('5'); buf_print(':');
  701.          for(i=0;i<BSIZE && b[4][i] != 0;i++) buf_print(b[4][i]);
  702.          break;
  703.       case 0x6d00: /* alt f6 */
  704.          buf_clear();
  705.          buf_print('B'); buf_print('6'); buf_print(':');
  706.          for(i=0;i<BSIZE && b[5][i] != 0;i++) buf_print(b[5][i]);
  707.          break;
  708.       case 0x6e00: /* alt f7 */
  709.          buf_clear();
  710.          buf_print('B'); buf_print('7'); buf_print(':');
  711.          for(i=0;i<BSIZE && b[6][i] != 0;i++) buf_print(b[6][i]);
  712.          break;
  713.       case 0x6f00: /* alt f8 */
  714.          buf_clear();
  715.          buf_print('B'); buf_print('8'); buf_print(':');
  716.          for(i=0;i<BSIZE && b[7][i] != 0;i++) buf_print(b[7][i]);
  717.          break;
  718.       case 0x7000: /* alt f9 */
  719.          buf_clear();
  720.          buf_print('B'); buf_print('9'); buf_print(':');
  721.          for(i=0;i<BSIZE && b[8][i] != 0;i++) buf_print(b[8][i]);
  722.          break;
  723.       case 0x7100: /* alt f10 */
  724.          buf_clear();
  725.          buf_print('B'); buf_print('1'); buf_print('0'); buf_print(':');
  726.          for(i=0;i<BSIZE && b[9][i] != 0;i++) buf_print(b[9][i]);
  727.          break;
  728.       default:
  729.          break;
  730.       }
  731.    }
  732. }
  733. void reader()
  734. {
  735.    FILE *f;
  736.    void analyze();
  737.    int c,i;
  738.    char s[256];
  739.    f = fopen("cw.ini","r");
  740.    while ((c = getc(f)) != EOF) {
  741.       ungetc(c,f);
  742.       i = 0;
  743.       while((c = getc(f)) != EOF) {
  744.          if (c == '\n' && i>0 && s[i-1] =='\\') {
  745.             i--;
  746.          } else {
  747.             if (c == '\n') break;
  748.             s[i++] = (char) c;
  749.          }
  750.       }
  751.       s[i] = '\0';
  752.       analyze(s);
  753.    }
  754.    fclose(f);
  755. }
  756. void analyze(s)
  757. char *s;
  758. {
  759.    FILE *fr;
  760.    FILE *fw;
  761.    int i,j,k,cc;
  762.    void xmt_print(),buf_print(),init(),end(),help();
  763.    char *c[8] = { "sound","read","receive","transmit"
  764.       ,"stretch","b","serial","?" };
  765.    char *helps = "commands are sound, read, receive, transmit,\
  766.  stretch, bn, serial, ?, type command ? for more help";
  767.    char *helpc[7] = { " sound on,  sound off,  sound nnnn  where nnnn= frequency",
  768.       "read filename,  where contents of filename are placed in send queue",
  769.       "receive manual, receive automatic, receive nn  where nn is manual speed",
  770.       "transmit on, transmit off, transmit nn where nn is speed",
  771.       "stretch nnnn,  reduces marks and increases spaces by nnnn*840 ns\
  772.  to compensate for pulse stretching in filters",
  773.       "bn text  fills buffer n with text",
  774.       "serial hhhh h  where hhhh is address in hex and h is vector in hex" };
  775.    for (i=0;i<8;i++) {
  776.       if (index(s,c[i]) != -1) break;
  777.    }
  778.    if (((i < 7 && i !=5) && index(s,"?") != -1) || (i == 5 && index(s,"?") == 2)) {
  779.       j = 0;
  780.       buf_print(' ');
  781.       while(helpc[i][j] != '\0') buf_print(helpc[i][j++]);
  782.       return;
  783.    }
  784.    switch (i) {
  785.    case 0:
  786.       if (index(&s[6],"on") != -1) {
  787.          sound.on = 1;
  788.       } else {
  789.          if (index(&s[6],"off") != -1) {
  790.             sound.on = 0;
  791.          } else {
  792.             sscanf(&s[6],"%d",&sound.frequency);
  793.             sound.low = (1193280L/sound.frequency)%256;
  794.             sound.high = (1193280L/sound.frequency)/256;
  795.             outportb(0x43,0xb6);
  796.             outportb(0x42,sound.low);
  797.             outportb(0x42,sound.high);
  798.          }
  799.       }
  800.       break;
  801.    case 1:
  802.       fr = fopen(&s[5],"r");
  803.       while ((cc = fgetc(fr)) != EOF) {
  804.          if (cc > 30) {
  805.             j = (send.in+1) % SQSIZE;
  806.             send.queue[j] = (char)cc;
  807.             send.in = j;
  808.             xmt_print((char)cc);
  809.          }
  810.       }
  811.       fclose(fr);
  812.       break;
  813.    case 2:
  814.       if(index(&s[7],"manual") != -1) {
  815.          autospeed = 0;
  816.          change = 8;
  817.       } else {
  818.          if (index(&s[7],"automatic") != -1) {
  819.             autospeed = 1;
  820.             change = 8;
  821.          } else {
  822.             sscanf(&s[7],"%d",&j);
  823.             if (autospeed == 0 ) {
  824.                dot2 = 2863872L/j;
  825.                for (k=0;k<SAMPLE;k++) {
  826.                   mark.sample[k] = dot2;
  827.                }
  828.                mark.sum = SAMPLE*dot2;
  829.                noise = dot2/40L+stretch;
  830.                change = 8;
  831.             }
  832.          }
  833.       }
  834.       break;
  835.    case 3:
  836.       if (index(&s[8],"on") != -1) {
  837.          send.on = 1;
  838.       } else {
  839.          if (index(&s[8],"off") != -1) {
  840.             send.on = 0;
  841.          } else {
  842.             sscanf(&s[8],"%d",&j);
  843.             send.alarm = max((2863872L*SPEEDUP)/(j*131078L),1L);
  844.             change = 8;
  845.          }
  846.       }
  847.       break;
  848.    case 4:
  849.       sscanf(&s[7],"%ld",&stretch);
  850.       break;
  851.    case 5:
  852.       sscanf(&s[1],"%d",&i);
  853.       if (i>10 || i<0) break;
  854.       if (i==10) j = 4; else j=3;
  855.       for(k=0;k<BSIZE && s[j] != '\0';k++) b[i-1][k] = s[j++];
  856.       break;
  857.    case 6:
  858.       end();
  859.       sscanf(&s[6],"%x %x",&serial.address,&serial.vector);
  860.       init();
  861.       help();
  862.       break;
  863.    case 7:
  864.       j = 0;
  865.       while(helps[j] != '\0') buf_print(helps[j++]);
  866.       break;
  867.    }
  868. }
  869. index(s,t)
  870. char s[],t[];
  871. /*
  872.  index from kernighan and ritchie
  873. */
  874. {
  875.    int i,j,k;
  876.    for (i=0;s[i] != '\0';i++) {
  877.       for (j=i,k=0;t[k]!='\0' && s[j]==t[k];j++,k++);
  878.       if (t[k] == '\0') return(i);
  879.    }
  880.    return(-1);
  881. }
  882. void clear()
  883. {
  884.    union REGS regs;
  885.    void cursor_now();
  886.    int x,y;
  887. /*
  888.  get video mode and clear screen
  889. */
  890.    regs.h.ah = 0xf;
  891.    (void) int86(0x10,®s,®s);
  892.    video.mode = regs.h.al;
  893.    video.width = regs.h.ah;
  894.    video.page  = regs.h.bh;
  895.    regs.h.ah = 0;
  896.    regs.h.al = video.mode;
  897.    (void) int86(0x10,®s,®s);
  898.    cursor_now(&x,&y);
  899.    wxmt.x = 0;
  900.    wxmt.y = 21;
  901.    wrcv.x = 0;
  902.    wrcv.y = 15;
  903.    wbuf.x = 0;
  904.    wbuf.y = 24;
  905. }
  906. void cursor(x,y)
  907. int x,y;
  908. {
  909.    union REGS regs;
  910.    regs.h.ah = 2;
  911.    regs.h.dh = y;
  912.    regs.h.dl = x;
  913.    regs.h.bh = video.page;
  914.    (void) int86(0x10,®s,®s);
  915. }
  916. void scroll(top,bottom)
  917. int top,bottom;
  918. {
  919.    union REGS regs;
  920.    regs.h.ah = 6;
  921.    regs.h.al = 1;
  922.    regs.h.ch = top;
  923.    regs.h.cl = 0;
  924.    regs.h.dh = bottom;
  925.    regs.h.dl = 79;
  926.    regs.h.bh = 7;
  927.    (void) int86(0x10,®s,®s);
  928. }
  929. void rcv_print(s)
  930. char *s;
  931. {
  932.    union REGS regs;
  933.    void scroll(),cursor(),type(),cursor_now();
  934.    char c;
  935.    cursor(wrcv.x,wrcv.y);
  936.    while ((c = *s++) != 0) {
  937.       if (++wrcv.x == (int) video.width) {
  938. /*
  939.  scroll screen
  940. */
  941.          scroll(6,15);
  942.          cursor(0,15);
  943.       }
  944.    type(c);
  945.    }
  946.    cursor_now(&wrcv.x,&wrcv.y);
  947. }
  948. void xmt_print(s)
  949. char s;
  950. {
  951.    union REGS regs;
  952.    void scroll(),cursor(),type(),cursor_now();
  953.    cursor(wxmt.x,wxmt.y);
  954.    if (++wxmt.x == (int) video.width) {
  955. /*
  956.  scroll screen
  957. */
  958.       if (wxmt.y == 21) {
  959.          scroll(17,21);
  960.          cursor(0,21);
  961.       } else {
  962.          cursor(0,++wxmt.y);
  963.       }
  964.    }
  965.    type(s);
  966.    cursor_now(&wxmt.x,&wxmt.y);
  967. }
  968. void buf_clear()
  969. {
  970.    void scroll();
  971.    scroll(23,24);
  972.    scroll(23,24);
  973.    wbuf.x = 0;
  974.    wbuf.y = 24;
  975.    cursor(0,24);
  976. }
  977. void buf_print(s)
  978. char s;
  979. {
  980.    union REGS regs;
  981.    void scroll(),cursor(),type(),cursor_now();
  982.    cursor(wbuf.x,wbuf.y);
  983.    if (++wbuf.x == (int) video.width) {
  984. /*
  985.  scroll screen
  986. */
  987.       if (wbuf.y == 24) {
  988.          scroll(23,24);
  989.          cursor(0,24);
  990.       } else {
  991.          cursor(0,++wbuf.y);
  992.       }
  993.    }
  994.    type(s);
  995.    cursor_now(&wbuf.x,&wbuf.y);
  996. }
  997. void type(c)
  998. char c;
  999. {
  1000.    union REGS regs;
  1001. /*
  1002.  write out character
  1003. */
  1004.       regs.h.ah = 0xe;
  1005.       regs.h.al = (unsigned char) c;
  1006.       regs.h.bl = 7;
  1007.       regs.h.bh = video.page;
  1008.       (void) int86(0x10,®s,®s);
  1009. }
  1010. void cursor_now(x,y)
  1011. int *x,*y;
  1012. {
  1013.    union REGS regs;
  1014.    regs.h.ah = 3;
  1015.    regs.h.bh = video.page;
  1016.    (void) int86(0x10,®s,®s);
  1017.    video.csize = regs.x.cx;
  1018.    *x = regs.h.dl;
  1019.    *y = regs.h.dh;
  1020. }
  1021. void cursor_off()
  1022. {
  1023.    union REGS regs;
  1024.    regs.h.ah = 1;
  1025.    regs.h.ch = 0x20;
  1026.    regs.h.bh = video.page;
  1027.    (void) int86(0x10,®s,®s);
  1028. }
  1029. void cursor_on()
  1030. {
  1031.    union REGS regs;
  1032.    regs.h.ah = 1;
  1033.    regs.x.cx = video.csize;
  1034.    regs.h.bh = video.page;
  1035.    (void) int86(0x10,®s,®s);
  1036. }
  1037. void help()
  1038. {
  1039.    void cursor_now(),cursor_on(),cursor_off(),cursor();
  1040.    int x,y;
  1041.    cursor_now(&x,&y);
  1042.    cursor_off();
  1043.    cursor(14,2);
  1044.    printf("CTRL fn mod bufn");
  1045.    cursor(14,3);
  1046.    printf("ALT fn view bufn");
  1047.    cursor(14,4);
  1048.    printf("Fn send bufn");
  1049.    cursor(32,0);
  1050.    printf("f1 sound");
  1051.    cursor(32,1);
  1052.    printf("f2 auto/man");
  1053.    cursor(32,2);
  1054.    printf("f3 rcv slower");
  1055.    cursor(32,3);
  1056.    printf("f4 rcv faster");
  1057.    cursor(32,4);
  1058.    printf("f5 xmt slower");
  1059.    cursor(48,0);
  1060.    printf("f6 xmt faster");
  1061.    cursor(48,1);
  1062.    printf("f7 xmt -> rcv");
  1063.    cursor(48,2);
  1064.    printf("f8 command");
  1065.    cursor(48,3);
  1066.    printf("f9 send/hold");
  1067.    cursor(48,4);
  1068.    printf("f10 exit");
  1069.    cursor(65,0);
  1070.    printf("f8 ? <cr> for");
  1071.    cursor(65,1);
  1072.    printf("command help");
  1073.    cursor(x,y);
  1074.    cursor_on();
  1075. }
  1076. void info(dot,dash,elsp,letsp,dot2)
  1077. double dot,dash,elsp,letsp;
  1078. long dot2;
  1079. {
  1080. /*
  1081.  write out statistics
  1082. */
  1083.    void cursor_now(),cursor_off(),cursor_on(),cursor();
  1084.    int x,y;
  1085.    double dotfac,dot2f,xmt;
  1086.    dotfac = 1.2*SAMPLE/840e-9;
  1087.    cursor_now(&x,&y);
  1088.    cursor_off();
  1089.    dot2f = 2*dotfac/(SAMPLE*dot2);
  1090.    dot = dotfac/dot;
  1091.    dash = 3.*dotfac/dash;
  1092.    elsp = dotfac/elsp;
  1093.    letsp = 3.*dotfac/letsp;
  1094.    xmt = 1.2*SPEEDUP/(send.alarm*65536.*840e-9);
  1095.    cursor(0,0);
  1096.    if (autospeed) {
  1097.       printf("auto\n");
  1098.    } else {
  1099.       printf("man \n");
  1100.    }
  1101.    printf("dot   = %4.1f\n",dot);
  1102.    printf("dash  = %4.1f\n",dash);
  1103.    printf("el sp = %4.1f\n",elsp);
  1104.    printf("lt sp = %4.1f\n",letsp);
  1105.    cursor(14,0);
  1106.    printf("rcv = %4.1f",dot2f);
  1107.    cursor(14,1);
  1108.    printf("xmt = %4.1f",xmt);
  1109.    cursor(x,y);
  1110.    cursor_on();
  1111. }
  1112. void init()
  1113. {
  1114. /*
  1115.  Set up timer chip to call interrupt IRQ0 SPEEDUP times faster than
  1116.  normal. Also change to mode 2.
  1117. */
  1118.    unsigned char i,low,high;
  1119.    long divisor = 65536;
  1120.    outportb(0x43,0xb6);
  1121.    outportb(0x42,sound.low);
  1122.    outportb(0x42,sound.high);
  1123.    disable();
  1124.    oldhtimevec = getvect(0x8);
  1125.    setvect(0x8,htimevec);
  1126.    divisor /= SPEEDUP;
  1127.    low = divisor & 0xff;
  1128.    high = (divisor >> 8) & 0xff;
  1129.    outportb(0x43,0x34);
  1130.    outportb(0x40,low);
  1131.    outportb(0x40,high);
  1132.    enable();
  1133.    timer.tick1 = newtick;
  1134.    timer.fast1 = 0;
  1135.    queue.countin = 0;
  1136.    queue.countout = 0;
  1137. /*
  1138.  save old interrupt vector and set new interrupt vector
  1139. */
  1140.    oldcomvec = getvect(8+serial.vector);
  1141.    setvect(8+serial.vector,comvec);
  1142. /*
  1143.  set interrupt on modem status change
  1144. */
  1145.    outportb(serial.address+1,8);
  1146. /*
  1147.  turn on interrupts on 8250, RTS low, no loopback
  1148. */
  1149.    outportb(serial.address+4,8);
  1150. /*
  1151.  enable interrupt on 8259
  1152. */
  1153.    i = inportb(0x21);
  1154.    i &= ~(1<<serial.vector);
  1155.    outportb(0x21,i);
  1156.    i = inportb(0x3f8+6);
  1157. }
  1158. void end()
  1159. {
  1160.    unsigned char i;
  1161.    unsigned char new;
  1162.    union REGS inregs,outregs;
  1163. /*
  1164.  disable com interrupts and reset vectors
  1165. */
  1166.    disable();
  1167.    setvect(0x8,oldhtimevec);
  1168.    outportb(0x43,0x36);
  1169.    outportb(0x40,0x0);
  1170.    outportb(0x40,0x0);
  1171.    enable();
  1172.    i = inportb(serial.address+4);
  1173.    i &= ~2;
  1174.    outportb(serial.address+4,i);
  1175.    outportb(serial.address+1,0);
  1176.    outportb(serial.address+4,0);
  1177.    i = inportb(0x21);
  1178.    i |= (1<<serial.vector);
  1179.    outportb(0x21,i);
  1180.    setvect(8+serial.vector,oldcomvec);
  1181.    new = inportb(0x61);
  1182.    new &= 0xfc;
  1183.    outportb(0x61,new);
  1184.    inregs.h.ah = 0;
  1185.    inregs.h.al = video.mode;
  1186.    (void) int86(0x10,&inregs,&outregs);
  1187. }
  1188. void interrupt comvec()
  1189. {
  1190.    char in;
  1191.    unsigned status;
  1192.    unsigned char lsb,msb,new;
  1193.    static long speed_factor = 65536L/SPEEDUP;
  1194. /*
  1195.  while interrupts to service
  1196. */
  1197.    while (((in = inportb(serial.address+2)) & 1) == 0) {
  1198. /*
  1199.  check if modem status changed
  1200. */
  1201.       if ((in & 6) == 0) {
  1202. /*
  1203.  get status and check if clear to send line has changed
  1204. */
  1205.          status = inportb(serial.address+6);
  1206.          if (!(status & 1)) break;
  1207. /*
  1208.  clear to send has changed so disable interrupts
  1209.  and get time since last change
  1210. */
  1211.          disable();
  1212. /*
  1213.  lock clock counter and get current count and get current bios
  1214.  tick count
  1215. */
  1216.          outportb(0x43,0);
  1217.          lsb = inportb(0x40);
  1218.          msb = inportb(0x40);
  1219.          timer.tick2 = newtick;
  1220.          enable();
  1221.          timer.fast2 = speed_factor-((((long)msb) << 8) + (long)lsb);
  1222. /*
  1223.  mstime contains number of 840 ns clock pulses since last
  1224.  change of clear to send and mark is 1 if key down 0 if key up
  1225. */
  1226.          queue.mstime[queue.countin] = ((timer.tick2-timer.tick1)*speed_factor)
  1227.             +(timer.fast2-timer.fast1);
  1228.          status = (status>>4) & 1;
  1229.          queue.mark[queue.countin++] = status;
  1230. /*
  1231.  bookkeeping for circular queue and old times
  1232. */
  1233.          queue.countin %= QSIZE;
  1234.          timer.fast1 = timer.fast2;
  1235.          timer.tick1 = timer.tick2;
  1236.          if (sound.on == 1) {
  1237.             new = inportb(0x61);
  1238.             new &= 0xfc;
  1239.             new |= 3*(status & 1);
  1240.             outportb(0x61,new);
  1241.          }
  1242.       }
  1243.    }
  1244. /*
  1245.  report interrupt serviced
  1246. */
  1247.    outportb(0x20,0x20);
  1248. }
  1249. void interrupt htimevec()
  1250. {
  1251. /*
  1252.  our replacement for int 8 (IRQ0) to send morse out serial port RTS line
  1253. */
  1254.    unsigned char i;
  1255.    static int left = 0;
  1256.    static int j;
  1257.    static long element;
  1258.    enable();
  1259.    printtick++;
  1260.    (++send.clock) %= send.alarm;
  1261. /*
  1262.  send.clock = 0 means time to send next element so
  1263.  find out if we have finished current letter if not
  1264.  work on it. If current letter done and we are still
  1265.  sending, get next letter to send and start it.
  1266. */
  1267.    if (send.clock == 0 && (send.on || left)) {
  1268.       i = inportb(serial.address+4);
  1269.       i &= ~2;
  1270.       if (left == 0 && send.out != send.in) {
  1271.          send.out++;
  1272.          send.out %= SQSIZE;
  1273.          j = (int) send.queue[send.out];
  1274.          left = sletter[j].length+3;
  1275.          element = sletter[j].element << 1;
  1276.       }
  1277.       if (left != 0) {
  1278.          left--;
  1279.          i |= (unsigned char) (element & 2L);
  1280.          element = element >> 1;
  1281.       }
  1282.       outportb(serial.address+4,i);
  1283.    }
  1284. /*
  1285.  Call old interrupt every SPEEDUP ticks. The old interrupt will tell 8259
  1286.  that interrupt was serviced so we don't.
  1287. */
  1288.    if (((++newtick) % SPEEDUP) == 0 ) {
  1289.       (*oldhtimevec)();
  1290.    } else {
  1291.       outportb(0x20,0x20);
  1292.    }
  1293. }
  1294.