home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
hamradio
/
cw.arc
/
CW.C
next >
Wrap
Text File
|
1988-10-22
|
39KB
|
1,294 lines
static char *notice =
"Copyright 1988 by Kevin Schmidt W9CF \
You may copy and distribute this program for noncommercial use.\
You may modify this program if this notice is left \
intact. Compiled object or executable files made from this or a \
modified version may only be distributed if accompanied \
by their source code. \
DISCLAIMER: \
Since this code plays around with the innards of the pc and I am \
not an expert on pc programming, you run this program at your own risk.";
/*
This program sends and receives morse code using
the pc serial port.
The code is written using TURBO C (R) Borland International.
It is coded for key down = +12v and key up = -12v.
The clear to send line is used for input and request to send for output.
It uses com1 on pc-at by default. For other ports or machines
change the serial port address and interrupt number.
The code has been tested on my 6MHZ ibm pc-at running dos 3.1
with CGA and serial/parallel adaptor and on a zenith z-181 portable.
Any other 80 column or greater display should work. Since the clock
chip is reprogrammed and serial port interrupts are used,
probably only hardware as well as bios level clones of the IBM pc will
run this code.
The program can be divided into three parts. The first part is an interrupt
handler for the serial port. The serial port is set to interrupt on
change of the modem status. In particular, this happens when the clear
to send line is changed. The interrupt handler simply records the
status of the clear to send line in queue.mark and the time since
the clear to send line last changed in queue.mstime and increments
the queue pointer countin. The serial port stuff is fairly standard
and I used the description given by of R. Jourdain,
"Programmer's Problem Solver for the IBM PC,XT, and AT",
(Prentice Hall, 1986, NY). The method used for timing the marks
and spaces is adapted from B. Sheppard, "High Performance Software
Analysis on the IBM PC", BYTE, January 1987.
The second part of the code is a replacement for the IRQ0 timer
interrupt. I change the timer to interrupt SPEEDUP times faster
than original. Every SPEEDUP interrupts the old interrupt handler is
called so that disk operations and the bios date should be unaffected.
The replacement interrupt handler takes characters from a queue
and keys the request to send line of the serial port. Note if
you only want to receive morse this replacement routine is not
necessary since microsecond timing accuracy can be obtained without
changing this interrupt just change SPEEDUP to 1. Note SPEEDUP
must be a power of 2.
The last part of the code is the human interface, which looks at
the receive queue and displays the result along with some statistics,
and also fills the transmit buffer.
Since both the receive and transmit pieces are interrupt driven, the
code can be tested by shorting the clear to send line to the request to
send line. This will loop back the sent morse to the morse decoder.
*/
#include <stdio.h>
#include <dos.h>
#include <bios.h>
#define max(A,B) ((A) > (B) ? (A) : (B))
#define min(A,B) ((A) < (B) ? (A) : (B))
#define MARK 0
#define SPACE 1
#define QSIZE 1024
#define SAMPLE 64
#define PRINTTIME 2000
#define SPEEDUP 16 /* must be > 0 and a power of 2 */
#define SQSIZE 4096
#define BSIZE 150
/*
video stuff
*/
static unsigned printtick = 0;
static struct video { unsigned char mode,page,width; int csize; } video;
static struct cpos { int x,y; } wxmt,wrcv,wbuf;
/*
define queue of times and whether mark or space
*/
static struct queue {
long mstime[QSIZE];
char mark[QSIZE];
int countin,countout; } queue;
/*
stuff to read time between interrupts
*/
static struct timer {
long tick1,tick2,fast1,fast2; } timer;
static struct sound { int on,frequency; unsigned char low,high; } sound;
/*
serial port address and vector -- these values are for com1 on PC-AT
they must be changed to use another serial port or on another machine
this can be done here or in cw.ini initialization file
*/
static struct serial { unsigned int address,vector; } serial = { 0x3f8, 4 };
static int autospeed = 1;
/*
following are tables of morse characters. c1 is table of characters of
length 1, c2 of length 2, etc.
think in binary. dot = 0, dash = 1 so e.g. c = dash dot dash dot
is entry number ten (eleventh starting from zero) for the characters
of length 4.
*/
static char *c0[1] = { "" };
static char *c1[2] = {"e","t"};
static char *c2[4] = {"i","a","n","m"};
static char *c3[8] = {"s","u","r","w","d","k","g","o"};
static char *c4[16] = {"h","v","f","X","l","X","p","j"
,"b","x","c","y","z","q","X","X"};
static char *c5[32] = {"5","4"," UNDERSTOOD ","3","X","X","X","2"
," WAIT ","X"," END OF MESSAGE ","X","X","X","X","1"
,"6","=","/","X","X"," START ","(","X"
,"7","X","X","X","8","X","9","0"};
static char *c6[64] = {" ERROR ","X","X","X","X"," 30 ","X","X"
,"X","$","X","X","?","_","X","X"
,"X","X","\"","X"," PARAGRAPH ",".","X","X"
,"X","X","X","X","X","X","X","X"
,"X","-","X","X","X","X","X","X"
,"X","X",";","X","X",")","X","X"
,"X","X","X",",","X","X","X","X"
,":","X","X","X","X","X","X","X"};
static char **c[7] = { c0,c1,c2,c3,c4,c5,c6 };
/*
new and old interrupt vector for serial port
and bios hardware timer interrupt
*/
static void interrupt comvec();
static void interrupt (*oldcomvec)();
static void interrupt htimevec();
static void interrupt (*oldhtimevec)();
static long newtick = 0;
static unsigned char b1[BSIZE],b2[BSIZE],b3[BSIZE],b4[BSIZE],b5[BSIZE];
static unsigned char b6[BSIZE],b7[BSIZE],b8[BSIZE],b9[BSIZE],b10[BSIZE];
static unsigned char *b[10] = { b1,b2,b3,b4,b5,b6,b7,b8,b9,b10 };
static int bufnow = 0;
static struct send { unsigned int clock,alarm;
unsigned char queue[SQSIZE];
unsigned int in,out;
int on; } send;
static struct sletter { long element; int length; } sletter[128] = {
0L, 0, 21845L,15, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0,
0L, 4, 0L, 0, 23901L,15, 0L, 0,
120277L,17, 0L, 0, 0L, 0, 122333L,19,
24023L,15, 482775L,19, 0L, 0, 5981L,13,
488823L,19, 30039L,15, 120669L,17, 5975L,13,
489335L,19, 122333L,17, 30581L,15, 7637L,13,
1877L,11, 341L, 9, 1367L,11, 5495L,13,
22391L,15, 96119L,17, 87927L,17, 95703L,17,
0L, 0, 7511L,13, 0L, 0, 22389L,15,
0L, 0, 29L, 5, 343L, 9, 1495L,11,
87L, 7, 1L, 1, 373L, 9, 375L, 9,
85L, 7, 5L, 3, 7645L,13, 471L, 9,
349L, 9, 119L, 7, 23L, 5, 1911L,11,
1501L,11, 7543L,13, 93L, 7, 21L, 5,
7L, 3, 117L, 7, 469L, 9, 477L, 9,
1879L,11, 7639L,13, 1399L,11, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 120693L,17,
0L, 0, 29L, 5, 343L, 9, 1495L,11,
87L, 7, 1L, 1, 373L, 9, 375L, 9,
85L, 7, 5L, 3, 7645L,13, 471L, 9,
349L, 9, 119L, 7, 23L, 5, 1911L,11,
1501L,11, 7543L,13, 93L, 7, 21L, 5,
7L, 3, 117L, 7, 469L, 9, 477L, 9,
1879L,11, 7639L,13, 1399L,11, 0L, 0,
0L, 0, 0L, 0, 0L, 0, 0L, 0 };
struct stats { long sample[SAMPLE]; int count; double sum; };
static struct stats mark,space,dot,dash,elsp,letsp;
static int change = 8;
static int done = 0;
static long dot2,noise;
static long stretch = 0L;
main()
{
int i,j,ii;
unsigned int letter = 0,size = 0;
void init(),end(),clear(),rcv_print(),info(),getkey(),cursor(),help();
void reader();
/*
set sound on and frequency at 800HZ then initialize sound and
interrupts
*/
sound.frequency = 800;
sound.on = 1;
sound.low = (1193280L/sound.frequency)%256;
sound.high = (1193280L/sound.frequency)/256;
send.clock = 0;
send.alarm = (100*SPEEDUP)/64;
send.on = 0;
send.in = send.out = 0;
init();
/*
set speed value for about 15wpm this should be ok for about
1/2 to twice this speed, and with luck will correct itself
for other speeds
*/
dot2 = 200000L;
noise = dot2/40L;
/*
get video mode stuff, clear screen, and set cursor position
*/
clear();
help();
/*
read initialization file to change any of above stuff
*/
reader();
/*
fill tables for statistics
*/
for (i=0;i<SAMPLE;i++) {
mark.sample[i] = dot2;
dot.sample[i] = elsp.sample[i] = dot2/2;
dash.sample[i] = letsp.sample[i] = 3*dot2/2;
}
mark.sum = SAMPLE*dot2;
dot.sum = elsp.sum = SAMPLE*dot2/2;
dash.sum = letsp.sum = SAMPLE*3*dot2/2;
mark.count = dot.count = dash.count = elsp.count = letsp.count = 0;
/*
loop until told to quit
*/
while(!done) {
/*
if key has been pressed, do something.
*/
if (bioskey(1)) {
getkey();
}
/*
update statistics printout if they have had a chance to change
*/
if (change >= 8) {
info(dot.sum,dash.sum,elsp.sum,letsp.sum,dot2);
change = 0;
}
/*
if no input for PRINTTIME clock ticks then print out last letter
*/
if (size != 0 && printtick > PRINTTIME) {
size = min(size,6);
letter &= 0x3f;
rcv_print(c[size][letter]);
rcv_print(" ");
letter = 0;
size = 0;
printtick = 0;
}
/*
start real stuff here - find out if some code has come in
*/
while(queue.countout != queue.countin) {
printtick = 0;
/*
ignore if it looks like noise from contact bounce
*/
if (queue.mstime[queue.countout] > noise) {
/*
check if mark or space and act accordingly
*/
switch (queue.mark[queue.countout]) {
case MARK:
queue.mstime[queue.countout] -= stretch;
size++;
if (autospeed) {
mark.sum -= mark.sample[mark.count];
mark.sum +=
(mark.sample[mark.count++] = queue.mstime[queue.countout]);
mark.count %= SAMPLE;
/*
set speed to be given by 2 dot times = average of last SAMPLE marks
this method should be replaced with a better statistical analysis
of both marks and spaces
*/
dot2 = (mark.sum)/(SAMPLE);
noise = dot2/40L+stretch;
}
change++;
/*
new mark so shift letter left 1 and fill rightmost bit with 0 for dot
and 1 for dash
*/
letter *= 2;
if (queue.mstime[queue.countout] > dot2) {
letter |= 1;
dash.sum -= dash.sample[dash.count];
dash.sum += (dash.sample[dash.count++] =
queue.mstime[queue.countout]);
dash.count %= SAMPLE;
} else {
dot.sum -= dot.sample[dot.count];
dot.sum += (dot.sample[dot.count++] =
queue.mstime[queue.countout]);
dot.count %= SAMPLE;
}
break;
/*
new space so find out if it is the end of a character element, letter or word
and act accordingly
*/
case SPACE:
queue.mstime[queue.countout] += stretch;
if (queue.mstime[queue.countout] > dot2) {
size = min(size,6);
letter &= 0x3f;
if (queue.mstime[queue.countout] > 5L*dot2/2L) {
rcv_print(c[size][letter]);
rcv_print(" ");
} else {
rcv_print(c[size][letter]);
letsp.sum -= letsp.sample[letsp.count];
letsp.sum += (letsp.sample[letsp.count++] =
queue.mstime[queue.countout]);
letsp.count %= SAMPLE;
}
letter = 0;
size = 0;
} else {
elsp.sum -= elsp.sample[elsp.count];
elsp.sum += (elsp.sample[elsp.count++] =
queue.mstime[queue.countout]);
elsp.count %= SAMPLE;
}
break;
}
}
/*
queue serviced so update pointer
*/
queue.countout++;
queue.countout %= QSIZE;
}
}
end();
}
void getkey()
{
/*
keyboard action -- find out why
*/
int j,i,cc,ccc;
unsigned char new;
char s[80];
static int point = 0;
void xmt_print(),cursor(),buf_clear(),buf_print(),analyze();
cc = bioskey(0);
ccc = cc & 0x007f;
/*
Check to see if it is an ascii character. If so add to
send queue and exit
*/
if (ccc > 0 && ccc < 128) {
/*
check for delete key -- try to take back character this could be done
better.
*/
if (bufnow == 0) {
if (ccc == 0x8) { /* delete key */
if (send.on) {
send.on = 0;
if (send.in != send.out) {
send.in = (send.in+SQSIZE-1) % SQSIZE;
if (wxmt.x == 0) {
if (wxmt.y != 17) {
wxmt.x = video.width-1;
wxmt.y -= 1;
}
} else {
wxmt.x -= 1;
}
cursor(wxmt.x,wxmt.y);
}
send.on = 1;
} else {
if (send.in != send.out) {
send.in = (send.in+SQSIZE-1) % SQSIZE;
if (wxmt.x == 0) {
wxmt.x = video.width-1;
wxmt.y -= 1;
} else {
wxmt.x -= 1;
}
cursor(wxmt.x,wxmt.y);
}
}
} else { /* other ascii just send*/
if (ccc > 31) {
j = (send.in+1) % SQSIZE;
send.queue[j] = (unsigned char)ccc;
send.in = j;
xmt_print((char) ccc);
}
}
} else {
switch(ccc) {
case 0x8:
if (wbuf.x == 0) {
if (wbuf.y != 23) {
wbuf.x = video.width-1;
wbuf.y -= 1;
}
} else {
wbuf.x -= 1;
}
cursor(wbuf.x,wbuf.y);
point--;
point = max(point,0);
break;
case 0x1b:
case 0x1a:
case 0x04:
case 0x0d:
point = min(point,BSIZE);
b[bufnow-1][point++] = '\0';
point = 0;
bufnow = 0;
break;
default:
point = min(point,BSIZE);
b[bufnow-1][point++] = (unsigned char) ccc;
buf_print((unsigned char) ccc);
}
}
} else {
/*
else see what we need to do
*/
if (bufnow != 0) {
b[bufnow-1][point++] = '\0';
point = 0;
bufnow = 0;
}
switch (cc) {
case 0x4800: /* up arror raise tone */
sound.frequency += 10;
sound.low = (1193280L/sound.frequency)%256;
sound.high = (1193280L/sound.frequency)/256;
outportb(0x43,0xb6);
outportb(0x42,sound.low);
outportb(0x42,sound.high);
break;
case 0x5000: /* down arrow lower tone*/
sound.frequency -= 10;
sound.low = (1193280L/sound.frequency)%256;
sound.high = (1193280L/sound.frequency)/256;
outportb(0x43,0xb6);
outportb(0x42,sound.low);
outportb(0x42,sound.high);
break;
case 0x3b00: /* f1 toggle sound*/
if (sound.on) {
sound.on = 0;
new = inportb(0x61);
new &= 0xfc;
outportb(0x61,new);
} else {
sound.on = 1;
outportb(0x43,0xb6);
outportb(0x42,sound.low);
outportb(0x42,sound.high);
}
break;
case 0x3c00: /* f2 toggle autospeed on/off */
if (autospeed) autospeed = 0; else autospeed = 1;
change = 8;
break;
case 0x3d00: /* f3 if manual speed increase it*/
if (autospeed == 0) {
dot2 = (dot2*10)/9;
for (i=0;i<SAMPLE;i++) {
mark.sample[i] = dot2;
}
mark.sum = SAMPLE*dot2;
noise = dot2/40L+stretch;
change = 8;
}
break;
case 0x3e00: /* f4 if manual speed decrease it*/
if (autospeed == 0) {
dot2 = (dot2*9)/10;
dot2 = max(10000L,dot2);
for (i=0;i<SAMPLE;i++) {
mark.sample[i] = dot2;
}
mark.sum = SAMPLE*dot2;
noise = dot2/40L+stretch;
change = 8;
}
break;
case 0x4400: /* f10 done */
done = 1;
break;
case 0x3f00: /* f5 decrease sending speed*/
i = send.alarm;
send.alarm *= 11;
send.alarm /= 10;
if (send.alarm == i) send.alarm++;
change = 8;
break;
case 0x4000: /* f6 increase sending speed*/
send.alarm = max((9*send.alarm)/10,1);
change = 8;
break;
case 0x4100: /* f7 set sending speed to receive speed */
send.alarm = max((dot2*SPEEDUP)/131078L,1L);
change = 8;
break;
case 0x4200: /* f8 command */
buf_clear();
buf_print('C'); buf_print('o'); buf_print('m');
buf_print('m'); buf_print('a'); buf_print('n');
buf_print('d'); buf_print(':'); buf_print(' ');
for(i=0;((ccc = bioskey(0)) & 0x7f) != '\015';) {
ccc &= 0x7f;
if (ccc == 0x8) {
if (i > 0) {
i--;
if (wbuf.x == 0) {
wbuf.x = video.width-1;
wbuf.y -= 1;
} else {
wbuf.x -= 1;
}
cursor(wbuf.x,wbuf.y);
}
} else {
s[i++] = (char) ccc;
buf_print(ccc);
}
}
s[i] = '\0';
analyze(s);
break;
case 0x4300: /* f9 toggle sending on/off */
send.on = 1-send.on;
break;
case 0x5400: /* shift f1 */
for (i=0;i<BSIZE && b1[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b1[i];
send.in = j;
xmt_print(b1[i]);
}
break;
case 0x5500: /* shift f2 */
for (i=0;i<BSIZE && b2[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b2[i];
send.in = j;
xmt_print(b2[i]);
}
break;
case 0x5600: /* shift f3 */
for (i=0;i<BSIZE && b3[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b3[i];
send.in = j;
xmt_print(b3[i]);
}
break;
case 0x5700: /* shift f4 */
for (i=0;i<BSIZE && b4[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b4[i];
send.in = j;
xmt_print(b4[i]);
}
break;
case 0x5800: /* shift f5 */
for (i=0;i<BSIZE && b5[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b5[i];
send.in = j;
xmt_print(b5[i]);
}
break;
case 0x5900: /* shift f6 */
for (i=0;i<BSIZE && b6[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b6[i];
send.in = j;
xmt_print(b6[i]);
}
break;
case 0x5a00: /* shift f7 */
for (i=0;i<BSIZE && b7[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b7[i];
send.in = j;
xmt_print(b7[i]);
}
break;
case 0x5b00: /* shift f8 */
for (i=0;i<BSIZE && b8[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b8[i];
send.in = j;
xmt_print(b8[i]);
}
break;
case 0x5c00: /* shift f9 */
for (i=0;i<BSIZE && b9[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b9[i];
send.in = j;
xmt_print(b9[i]);
}
break;
case 0x5d00: /* shift f10 */
for (i=0;i<BSIZE && b10[i] != '\0';i++) {
j = (send.in+1) % SQSIZE;
send.queue[j] = b10[i];
send.in = j;
xmt_print(b10[i]);
}
break;
case 0x5e00: /* ctrl f1 */
buf_clear();
buf_print('B'); buf_print('1'); buf_print(':');
bufnow = 1;
break;
case 0x5f00: /* ctrl f2 */
buf_clear();
buf_print('B'); buf_print('2'); buf_print(':');
bufnow = 2;
break;
case 0x6000: /* ctrl f3 */
buf_clear();
buf_print('B'); buf_print('3'); buf_print(':');
bufnow = 3;
break;
case 0x6100: /* ctrl f4 */
buf_clear();
buf_print('B'); buf_print('4'); buf_print(':');
bufnow = 4;
break;
case 0x6200: /* ctrl f5 */
buf_clear();
buf_print('B'); buf_print('5'); buf_print(':');
bufnow = 5;
break;
case 0x6300: /* ctrl f6 */
buf_clear();
buf_print('B'); buf_print('6'); buf_print(':');
bufnow = 6;
break;
case 0x6400: /* ctrl f7 */
buf_clear();
buf_print('B'); buf_print('7'); buf_print(':');
bufnow = 7;
break;
case 0x6500: /* ctrl f8 */
buf_clear();
buf_print('B'); buf_print('8'); buf_print(':');
bufnow = 8;
break;
case 0x6600: /* ctrl f9 */
buf_clear();
buf_print('B'); buf_print('9'); buf_print(':');
bufnow = 9;
break;
case 0x6700: /* ctrl f10 */
buf_clear();
buf_print('B'); buf_print('1'); buf_print('0'); buf_print(':');
bufnow = 10;
break;
case 0x6800: /* alt f1 */
buf_clear();
buf_print('B'); buf_print('1'); buf_print(':');
for(i=0;i<BSIZE && b[0][i] != 0;i++) buf_print(b[0][i]);
break;
case 0x6900: /* alt f2 */
buf_clear();
buf_print('B'); buf_print('2'); buf_print(':');
for(i=0;i<BSIZE && b[1][i] != 0;i++) buf_print(b[1][i]);
break;
case 0x6a00: /* alt f3 */
buf_clear();
buf_print('B'); buf_print('3'); buf_print(':');
for(i=0;i<BSIZE && b[2][i] != 0;i++) buf_print(b[2][i]);
break;
case 0x6b00: /* alt f4 */
buf_clear();
buf_print('B'); buf_print('4'); buf_print(':');
for(i=0;i<BSIZE && b[3][i] != 0;i++) buf_print(b[3][i]);
break;
case 0x6c00: /* alt f5 */
buf_clear();
buf_print('B'); buf_print('5'); buf_print(':');
for(i=0;i<BSIZE && b[4][i] != 0;i++) buf_print(b[4][i]);
break;
case 0x6d00: /* alt f6 */
buf_clear();
buf_print('B'); buf_print('6'); buf_print(':');
for(i=0;i<BSIZE && b[5][i] != 0;i++) buf_print(b[5][i]);
break;
case 0x6e00: /* alt f7 */
buf_clear();
buf_print('B'); buf_print('7'); buf_print(':');
for(i=0;i<BSIZE && b[6][i] != 0;i++) buf_print(b[6][i]);
break;
case 0x6f00: /* alt f8 */
buf_clear();
buf_print('B'); buf_print('8'); buf_print(':');
for(i=0;i<BSIZE && b[7][i] != 0;i++) buf_print(b[7][i]);
break;
case 0x7000: /* alt f9 */
buf_clear();
buf_print('B'); buf_print('9'); buf_print(':');
for(i=0;i<BSIZE && b[8][i] != 0;i++) buf_print(b[8][i]);
break;
case 0x7100: /* alt f10 */
buf_clear();
buf_print('B'); buf_print('1'); buf_print('0'); buf_print(':');
for(i=0;i<BSIZE && b[9][i] != 0;i++) buf_print(b[9][i]);
break;
default:
break;
}
}
}
void reader()
{
FILE *f;
void analyze();
int c,i;
char s[256];
f = fopen("cw.ini","r");
while ((c = getc(f)) != EOF) {
ungetc(c,f);
i = 0;
while((c = getc(f)) != EOF) {
if (c == '\n' && i>0 && s[i-1] =='\\') {
i--;
} else {
if (c == '\n') break;
s[i++] = (char) c;
}
}
s[i] = '\0';
analyze(s);
}
fclose(f);
}
void analyze(s)
char *s;
{
FILE *fr;
FILE *fw;
int i,j,k,cc;
void xmt_print(),buf_print(),init(),end(),help();
char *c[8] = { "sound","read","receive","transmit"
,"stretch","b","serial","?" };
char *helps = "commands are sound, read, receive, transmit,\
stretch, bn, serial, ?, type command ? for more help";
char *helpc[7] = { " sound on, sound off, sound nnnn where nnnn= frequency",
"read filename, where contents of filename are placed in send queue",
"receive manual, receive automatic, receive nn where nn is manual speed",
"transmit on, transmit off, transmit nn where nn is speed",
"stretch nnnn, reduces marks and increases spaces by nnnn*840 ns\
to compensate for pulse stretching in filters",
"bn text fills buffer n with text",
"serial hhhh h where hhhh is address in hex and h is vector in hex" };
for (i=0;i<8;i++) {
if (index(s,c[i]) != -1) break;
}
if (((i < 7 && i !=5) && index(s,"?") != -1) || (i == 5 && index(s,"?") == 2)) {
j = 0;
buf_print(' ');
while(helpc[i][j] != '\0') buf_print(helpc[i][j++]);
return;
}
switch (i) {
case 0:
if (index(&s[6],"on") != -1) {
sound.on = 1;
} else {
if (index(&s[6],"off") != -1) {
sound.on = 0;
} else {
sscanf(&s[6],"%d",&sound.frequency);
sound.low = (1193280L/sound.frequency)%256;
sound.high = (1193280L/sound.frequency)/256;
outportb(0x43,0xb6);
outportb(0x42,sound.low);
outportb(0x42,sound.high);
}
}
break;
case 1:
fr = fopen(&s[5],"r");
while ((cc = fgetc(fr)) != EOF) {
if (cc > 30) {
j = (send.in+1) % SQSIZE;
send.queue[j] = (char)cc;
send.in = j;
xmt_print((char)cc);
}
}
fclose(fr);
break;
case 2:
if(index(&s[7],"manual") != -1) {
autospeed = 0;
change = 8;
} else {
if (index(&s[7],"automatic") != -1) {
autospeed = 1;
change = 8;
} else {
sscanf(&s[7],"%d",&j);
if (autospeed == 0 ) {
dot2 = 2863872L/j;
for (k=0;k<SAMPLE;k++) {
mark.sample[k] = dot2;
}
mark.sum = SAMPLE*dot2;
noise = dot2/40L+stretch;
change = 8;
}
}
}
break;
case 3:
if (index(&s[8],"on") != -1) {
send.on = 1;
} else {
if (index(&s[8],"off") != -1) {
send.on = 0;
} else {
sscanf(&s[8],"%d",&j);
send.alarm = max((2863872L*SPEEDUP)/(j*131078L),1L);
change = 8;
}
}
break;
case 4:
sscanf(&s[7],"%ld",&stretch);
break;
case 5:
sscanf(&s[1],"%d",&i);
if (i>10 || i<0) break;
if (i==10) j = 4; else j=3;
for(k=0;k<BSIZE && s[j] != '\0';k++) b[i-1][k] = s[j++];
break;
case 6:
end();
sscanf(&s[6],"%x %x",&serial.address,&serial.vector);
init();
help();
break;
case 7:
j = 0;
while(helps[j] != '\0') buf_print(helps[j++]);
break;
}
}
index(s,t)
char s[],t[];
/*
index from kernighan and ritchie
*/
{
int i,j,k;
for (i=0;s[i] != '\0';i++) {
for (j=i,k=0;t[k]!='\0' && s[j]==t[k];j++,k++);
if (t[k] == '\0') return(i);
}
return(-1);
}
void clear()
{
union REGS regs;
void cursor_now();
int x,y;
/*
get video mode and clear screen
*/
regs.h.ah = 0xf;
(void) int86(0x10,®s,®s);
video.mode = regs.h.al;
video.width = regs.h.ah;
video.page = regs.h.bh;
regs.h.ah = 0;
regs.h.al = video.mode;
(void) int86(0x10,®s,®s);
cursor_now(&x,&y);
wxmt.x = 0;
wxmt.y = 21;
wrcv.x = 0;
wrcv.y = 15;
wbuf.x = 0;
wbuf.y = 24;
}
void cursor(x,y)
int x,y;
{
union REGS regs;
regs.h.ah = 2;
regs.h.dh = y;
regs.h.dl = x;
regs.h.bh = video.page;
(void) int86(0x10,®s,®s);
}
void scroll(top,bottom)
int top,bottom;
{
union REGS regs;
regs.h.ah = 6;
regs.h.al = 1;
regs.h.ch = top;
regs.h.cl = 0;
regs.h.dh = bottom;
regs.h.dl = 79;
regs.h.bh = 7;
(void) int86(0x10,®s,®s);
}
void rcv_print(s)
char *s;
{
union REGS regs;
void scroll(),cursor(),type(),cursor_now();
char c;
cursor(wrcv.x,wrcv.y);
while ((c = *s++) != 0) {
if (++wrcv.x == (int) video.width) {
/*
scroll screen
*/
scroll(6,15);
cursor(0,15);
}
type(c);
}
cursor_now(&wrcv.x,&wrcv.y);
}
void xmt_print(s)
char s;
{
union REGS regs;
void scroll(),cursor(),type(),cursor_now();
cursor(wxmt.x,wxmt.y);
if (++wxmt.x == (int) video.width) {
/*
scroll screen
*/
if (wxmt.y == 21) {
scroll(17,21);
cursor(0,21);
} else {
cursor(0,++wxmt.y);
}
}
type(s);
cursor_now(&wxmt.x,&wxmt.y);
}
void buf_clear()
{
void scroll();
scroll(23,24);
scroll(23,24);
wbuf.x = 0;
wbuf.y = 24;
cursor(0,24);
}
void buf_print(s)
char s;
{
union REGS regs;
void scroll(),cursor(),type(),cursor_now();
cursor(wbuf.x,wbuf.y);
if (++wbuf.x == (int) video.width) {
/*
scroll screen
*/
if (wbuf.y == 24) {
scroll(23,24);
cursor(0,24);
} else {
cursor(0,++wbuf.y);
}
}
type(s);
cursor_now(&wbuf.x,&wbuf.y);
}
void type(c)
char c;
{
union REGS regs;
/*
write out character
*/
regs.h.ah = 0xe;
regs.h.al = (unsigned char) c;
regs.h.bl = 7;
regs.h.bh = video.page;
(void) int86(0x10,®s,®s);
}
void cursor_now(x,y)
int *x,*y;
{
union REGS regs;
regs.h.ah = 3;
regs.h.bh = video.page;
(void) int86(0x10,®s,®s);
video.csize = regs.x.cx;
*x = regs.h.dl;
*y = regs.h.dh;
}
void cursor_off()
{
union REGS regs;
regs.h.ah = 1;
regs.h.ch = 0x20;
regs.h.bh = video.page;
(void) int86(0x10,®s,®s);
}
void cursor_on()
{
union REGS regs;
regs.h.ah = 1;
regs.x.cx = video.csize;
regs.h.bh = video.page;
(void) int86(0x10,®s,®s);
}
void help()
{
void cursor_now(),cursor_on(),cursor_off(),cursor();
int x,y;
cursor_now(&x,&y);
cursor_off();
cursor(14,2);
printf("CTRL fn mod bufn");
cursor(14,3);
printf("ALT fn view bufn");
cursor(14,4);
printf("Fn send bufn");
cursor(32,0);
printf("f1 sound");
cursor(32,1);
printf("f2 auto/man");
cursor(32,2);
printf("f3 rcv slower");
cursor(32,3);
printf("f4 rcv faster");
cursor(32,4);
printf("f5 xmt slower");
cursor(48,0);
printf("f6 xmt faster");
cursor(48,1);
printf("f7 xmt -> rcv");
cursor(48,2);
printf("f8 command");
cursor(48,3);
printf("f9 send/hold");
cursor(48,4);
printf("f10 exit");
cursor(65,0);
printf("f8 ? <cr> for");
cursor(65,1);
printf("command help");
cursor(x,y);
cursor_on();
}
void info(dot,dash,elsp,letsp,dot2)
double dot,dash,elsp,letsp;
long dot2;
{
/*
write out statistics
*/
void cursor_now(),cursor_off(),cursor_on(),cursor();
int x,y;
double dotfac,dot2f,xmt;
dotfac = 1.2*SAMPLE/840e-9;
cursor_now(&x,&y);
cursor_off();
dot2f = 2*dotfac/(SAMPLE*dot2);
dot = dotfac/dot;
dash = 3.*dotfac/dash;
elsp = dotfac/elsp;
letsp = 3.*dotfac/letsp;
xmt = 1.2*SPEEDUP/(send.alarm*65536.*840e-9);
cursor(0,0);
if (autospeed) {
printf("auto\n");
} else {
printf("man \n");
}
printf("dot = %4.1f\n",dot);
printf("dash = %4.1f\n",dash);
printf("el sp = %4.1f\n",elsp);
printf("lt sp = %4.1f\n",letsp);
cursor(14,0);
printf("rcv = %4.1f",dot2f);
cursor(14,1);
printf("xmt = %4.1f",xmt);
cursor(x,y);
cursor_on();
}
void init()
{
/*
Set up timer chip to call interrupt IRQ0 SPEEDUP times faster than
normal. Also change to mode 2.
*/
unsigned char i,low,high;
long divisor = 65536;
outportb(0x43,0xb6);
outportb(0x42,sound.low);
outportb(0x42,sound.high);
disable();
oldhtimevec = getvect(0x8);
setvect(0x8,htimevec);
divisor /= SPEEDUP;
low = divisor & 0xff;
high = (divisor >> 8) & 0xff;
outportb(0x43,0x34);
outportb(0x40,low);
outportb(0x40,high);
enable();
timer.tick1 = newtick;
timer.fast1 = 0;
queue.countin = 0;
queue.countout = 0;
/*
save old interrupt vector and set new interrupt vector
*/
oldcomvec = getvect(8+serial.vector);
setvect(8+serial.vector,comvec);
/*
set interrupt on modem status change
*/
outportb(serial.address+1,8);
/*
turn on interrupts on 8250, RTS low, no loopback
*/
outportb(serial.address+4,8);
/*
enable interrupt on 8259
*/
i = inportb(0x21);
i &= ~(1<<serial.vector);
outportb(0x21,i);
i = inportb(0x3f8+6);
}
void end()
{
unsigned char i;
unsigned char new;
union REGS inregs,outregs;
/*
disable com interrupts and reset vectors
*/
disable();
setvect(0x8,oldhtimevec);
outportb(0x43,0x36);
outportb(0x40,0x0);
outportb(0x40,0x0);
enable();
i = inportb(serial.address+4);
i &= ~2;
outportb(serial.address+4,i);
outportb(serial.address+1,0);
outportb(serial.address+4,0);
i = inportb(0x21);
i |= (1<<serial.vector);
outportb(0x21,i);
setvect(8+serial.vector,oldcomvec);
new = inportb(0x61);
new &= 0xfc;
outportb(0x61,new);
inregs.h.ah = 0;
inregs.h.al = video.mode;
(void) int86(0x10,&inregs,&outregs);
}
void interrupt comvec()
{
char in;
unsigned status;
unsigned char lsb,msb,new;
static long speed_factor = 65536L/SPEEDUP;
/*
while interrupts to service
*/
while (((in = inportb(serial.address+2)) & 1) == 0) {
/*
check if modem status changed
*/
if ((in & 6) == 0) {
/*
get status and check if clear to send line has changed
*/
status = inportb(serial.address+6);
if (!(status & 1)) break;
/*
clear to send has changed so disable interrupts
and get time since last change
*/
disable();
/*
lock clock counter and get current count and get current bios
tick count
*/
outportb(0x43,0);
lsb = inportb(0x40);
msb = inportb(0x40);
timer.tick2 = newtick;
enable();
timer.fast2 = speed_factor-((((long)msb) << 8) + (long)lsb);
/*
mstime contains number of 840 ns clock pulses since last
change of clear to send and mark is 1 if key down 0 if key up
*/
queue.mstime[queue.countin] = ((timer.tick2-timer.tick1)*speed_factor)
+(timer.fast2-timer.fast1);
status = (status>>4) & 1;
queue.mark[queue.countin++] = status;
/*
bookkeeping for circular queue and old times
*/
queue.countin %= QSIZE;
timer.fast1 = timer.fast2;
timer.tick1 = timer.tick2;
if (sound.on == 1) {
new = inportb(0x61);
new &= 0xfc;
new |= 3*(status & 1);
outportb(0x61,new);
}
}
}
/*
report interrupt serviced
*/
outportb(0x20,0x20);
}
void interrupt htimevec()
{
/*
our replacement for int 8 (IRQ0) to send morse out serial port RTS line
*/
unsigned char i;
static int left = 0;
static int j;
static long element;
enable();
printtick++;
(++send.clock) %= send.alarm;
/*
send.clock = 0 means time to send next element so
find out if we have finished current letter if not
work on it. If current letter done and we are still
sending, get next letter to send and start it.
*/
if (send.clock == 0 && (send.on || left)) {
i = inportb(serial.address+4);
i &= ~2;
if (left == 0 && send.out != send.in) {
send.out++;
send.out %= SQSIZE;
j = (int) send.queue[send.out];
left = sletter[j].length+3;
element = sletter[j].element << 1;
}
if (left != 0) {
left--;
i |= (unsigned char) (element & 2L);
element = element >> 1;
}
outportb(serial.address+4,i);
}
/*
Call old interrupt every SPEEDUP ticks. The old interrupt will tell 8259
that interrupt was serviced so we don't.
*/
if (((++newtick) % SPEEDUP) == 0 ) {
(*oldhtimevec)();
} else {
outportb(0x20,0x20);
}
}