/******************************************************/
/* MORSE v1.10 */
/* CW trainer for Soundblaster */
/******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <bios.h>
#include <conio.h>
#include <string.h>
#include <alloc.h>
#include <ctype.h>
#include <time.h>
#include <mem.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include <sb_voice.h>
#include <edgets.h>
#include "morseisr.h"
/* #pragma inline; */
#define MORSE_VERSION "1.10"
#define MORSEBUFSIZE 128 /* Number of entries in morse data buffer */
#define ONOFF 0x8000 /* Top bit controls tone on or off */
#define TRUE 1
#define FALSE 0
unsigned int morsebuf[MORSEBUFSIZE]; /* Communication between */
/* mainline and int 8 stuff */
unsigned int inptr = 0;
volatile unsigned int outptr = 0; /* Offsets into morsebuf */
int minlength,maxlength;
int showstat=0;
char *shwstatstr[2]={"Hide","Show"};
char *itonestr[2]={"Off","On"};
char *noisestr[3]={"None", "Hiss", "Pile-Up"};
int mastervol,beepvol,noisevol,lineinvol;
int wpm;
int dah, dit, dit_spacing, letter_spacing, word_spacing;
int noisetype;
char txtfname[81];
int numsamples;
char far *sample[100];
unsigned char letters[] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ���0123456789.,?!:\"'()/-*=~�+@"
};
unsigned char letters2[56];
unsigned char charstat[56];
int numchars;
unsigned int freq,ifreq;
unsigned char beep_on_val, beep_off_val, itone_on_val, itone_off_val;
unsigned int itonestat;
/* Pure sinus tone */
char beep[16] = {
0X21,0X21,0X00,0X00,0XC0,0XC0,0XFC,0XFC,
0X00,0X00,0X01,0X00,0X00,0X00,0X00,0X00
};
extern unsigned int ct_io_addx;
extern void fm_write(unsigned char reg,unsigned char data);
FILE* cfgfile;
char cfgfname[81];
unsigned char char_sent[32], char_end[32], char_to_write;
unsigned int char_head = 0, char_tail = 0;
int do_write_char = FALSE;
static unsigned char morsecode[256][7] = {
"","","", "", "", "", "", "w", /* 0 to 7 */
"","w", "w", "", "w", "", "", "", /* 8 to 15 */
"", "", "", "", "", "", "", "", /* 16 to 23 */
"", "", "", "", "", "", "", "", /* 24 to 31 */
"w", "..--.", ".-..-.", "", "", "", "", ".----.", /* ' ' to ''' */
"-.--.", "-.--.-", "..l..", ".-.-.", "-..-.", /* '(' to ... */
"-..-", ".-.-.-", "-..-.", /* ...'/' */
"-----", ".----", "..---", "...--", "....-", /* 0 to 4 */
".....", "-....", "--...", "---..", "----.", /* 5 to 9 */
"---...", "", "", "-...-", "", "..--..", "...-.-", /* ':' to '@' */
".-", "-...", "-.-.", "-..", ".", /* 'A' to 'E' */
"..-.", "--.", "....", "..", ".---", /* 'F' to 'J' */
"-.-", ".-..", "--", "-.", "---", /* 'K' to 'O' */
".--.", "--.-", ".-.", "...", "-", /* 'P' to 'T' */
"..-", "...-", ".--", "-..-", "-.--", "--..", /* 'U' to 'Z' */
"", "-..-.", "", "", "", "", /* '[' to '`' */
".-", "-...", "-.-.", "-..", ".", /* 'a' to 'e' */
"..-.", "--.", "....", "..", ".---", /* 'f' to 'j' */
"-.-", ".-..", "--", "-.", "---", /* 'k' to 'o' */
".--.", "--.-", ".-.", "...", "-", /* 'p' to 't' */
"..-", "...-", ".--", "-..-", "-.--", "--..", /* 'u' to 'z' */
"", "", "", "-.-.-", "", /* '{' to Del */
"","","","",".-.-","",".--.-","", /* 128 - 135 */
"","","","","","",".-.-",".--.-", /* 136 - 143 */
"","","","","---.","","","", /* 144 - 151 */
"","---.","","","","","","", /* 152 - 159 */
"","","","","","","","", /* 160 - 167 */
"","","","","","","","", /* 168 - 175 */
"","","","","","","","", /* 176 - 183 */
"","","","","","","","", /* 184 - 191 */
"","","","","","","","", /* 192 - 199 */
"","","","","","","","", /* 200 - 207 */
"","","","","","","","", /* 208 - 215 */
"","","","","","","","", /* 216 - 223 */
"","","","","","","","", /* 224 - 231 */
"","","","","","","","", /* 232 - 239 */
"","","","","","","","", /* 240 - 247 */
"","","",".-...","","","","" /* 248 - 255 */
};
main(int argc, char **argv)
{
int key;
int i;
if(! init())
exit(1);
while(1) {
key = getch();
switch(key){
case '1': random_text(0); break;
case '2': random_text(1); break;
case '3': text_from_file(); break;
case 'c': select_characters(); break;
case 'm': set_minmax(); break;
case 'w': set_wpm(); break;
case 'f': set_tone_freq(); break;
case 'i': set_itone(); break;
case 'h': show_hide(); break;
case 'b': set_backg_noise(); break;
case 'v': set_volumes(); break;
case 's': save_config(); break;
case 'l': load_config(); break;
case 'd': list_files(); break;
case 'q': quit();
}
draw_menu();
}
}
init()
{
int i, handle;
FILE* f;
unsigned int vocsize;
unsigned numread,segp;
if(! sb_voice_init())
return 0;
strcpy(cfgfname, "MORSE.CFG");
if(cfgfile = fopen(cfgfname,"rb"))
load_config2();
else {
wpm = 20;
for(i = 0; i < 56; i++)
charstat[i] = 1;
minlength=2;
maxlength=5;
mastervol=12;
beepvol=15;
noisevol=8;
noisetype=0;
itonestat=0;
freq = 4;
ifreq = 6;
}
numchars = 0;
for(i = 0; i < 56; i++) {
if (charstat[i])
letters2[numchars++] = letters[i];
}
randomize();
numsamples = 0;
if((handle = _open("MORSE.DAT", O_RDONLY)) == -1) {
printf("Error opening MORSE.DAT\n");
sb_voice_exit();
exit(1);
}
while(1){
DosRead(handle,(char far*)&vocsize,2,&numread);
if(! numread) break;
if(allocmem((unsigned)((vocsize+15) >> 4),&segp) != -1) {
printf("Malloc error!\n");
sb_voice_exit();
return 0;
}
FP_SEG(sample[numsamples]) = segp;
FP_OFF(sample[numsamples]) = 0 ;
DosRead(handle,sample[numsamples],vocsize,&numread);
numsamples++;
}
close(handle);
inst_morse_isr();
init_beep();
set_tone_freq2();
set_volume2();
textattr(0x1e);
hidecur();
clrscr();
draw_menu();
set_wpm2();
return 1;
}
quit()
{
int i, k = 0;
printf_at(2, 24, "Do you really want to quit (y/n)?");
if(yesno()) {
textattr(0x07);
clrscr();
for(i = 0; i < numsamples; i++)
freemem(FP_SEG(sample[i]));
rest_morse_isr();
sb_voice_exit();
showcur();
exit(0);
}
}
draw_menu()
{
clrscr();
printf("\n");
printf(" ������������������������������������������Ŀ\n");
printf(" � MORSE v%s by Mats Petersson SM5SXL �\n", MORSE_VERSION);
printf(" ��������������������������������������������\n\n\n");
printf(" 1 Groups of 5 with random characters\n");
printf(" 2 Variable length groups with random characters\n");
printf(" 3 Text from file\n");
printf(" C Select characters\n");
printf(" M Set min/max length for variable length groups\n");
printf(" W Set speed in WPM (%d)\n",wpm);
printf(" F Set tone frequencies (CW = %d, Interference = %d)\n", freq, ifreq);
printf(" I Interference tone on/off (%s)\n",itonestr[itonestat]);
printf(" H Show/hide characters (%s)\n",shwstatstr[showstat]);
printf(" B Set type of background noise (%s)\n",noisestr[noisetype]);
printf(" V Set volume levels\n");
printf(" S Save configuration\n");
printf(" L Load configuration\n");
printf(" D Dir (List files)\n");
printf(" Q Quit\n");
}
set_minmax()
{
get_option(2, 24, "Min length: ", "Invalid length",
&minlength, 1, 80, 2, TRUE, FALSE);
get_option(2, 24, "Max length: ", "Invalid length",
&maxlength, 1, 80, 2, TRUE, FALSE);
}
random_text(int mode)
{
unsigned char rndstring[82];
unsigned int i, col = 1;
int strlength, linelength = 0, first = TRUE;
textattr(0x07);
clrscr();
delay(500);
if(noisetype)
switch_on_noise();
if(itonestat)
switch_on_itone();
while(! kbhit()) {
if(mode == 0)
strlength = 5;
else
strlength = random(maxlength-(minlength-1)) +
minlength;
for(i = 0; i < strlength; i++)
rndstring[i] = letters2[random(numchars)];
rndstring[strlength] = 0;
if(first) {
linelength += strlength;
first = FALSE;
} else {
linelength += (strlength + 1);
if(linelength > 80) {
out_char(10);
linelength = strlength;
} else
out_char(' ');
}
out_str(rndstring);
}
getch();
stopmorse();
if(itonestat)
switch_off_itone();
if(noisetype)
switch_off_noise();
textattr(0x1e);
}
text_from_file()
{
FILE* f;
char filestr[81], tmp[81];
printf_at(2, 24, "Text file to send: ");
getstr(txtfname, 80, 0);
if(! strlen(txtfname))
return;
if(! (f = fopen(txtfname, "rt"))) {
sprintf(tmp, "Can't find %s!", txtfname);
printf_at(2, 24, tmp);
delay(2000);
return;
}
textattr(0x07);
clrscr();
if(noisetype)
switch_on_noise();
if(itonestat)
switch_on_itone();
while(fgets(filestr,80,f) != NULL) {
out_str(filestr);
if(kbhit()) break;
}
fclose(f);
while(inptr != outptr) { /* Wait for buffer to become empty */
if(showstat) chk_for_write();
if(kbhit()) {
getch();
break;
}
}
stopmorse();
if(itonestat)
switch_off_itone();
if(noisetype)
switch_off_noise();
textattr(0x1e);
}
out_str(unsigned char *string)
{
int i;
for(i = 0; i < strlen(string); i++) {
out_char(string[i]);
if(kbhit()) return;
}
}
out_char(unsigned int ascii_char) {
unsigned char ch;
unsigned char *cp;
unsigned int was_char;
cp = morsecode[ascii_char];
was_char = FALSE;
while ((ch = *cp++) != '\0') {
switch (ch) {
case 'l' :
putmorse(letter_spacing);
break;
case 'w' :
putmorse(word_spacing);
break;
case '.' :
putmorse(dit | ONOFF);
putmorse(dit_spacing);
was_char = TRUE;
break;
case '-' :
case '_' :
putmorse(dah | ONOFF);
putmorse(dit_spacing);
was_char = TRUE;
break;
}
}
if(showstat) {
char_sent[char_head] = ascii_char;
char_end[char_head] = inptr;
if(++char_head == 32)
char_head = 0;
}
if (was_char)
putmorse(letter_spacing);
}
putmorse(unsigned int codeval)
{
unsigned int tempptr;
if(kbhit()) return;
if(showstat) chk_for_write();
tempptr = inptr + 1;
if (tempptr >= MORSEBUFSIZE)
tempptr = 0;
while (outptr == tempptr) { /* Wait for space in buffer */
if(kbhit()) return;
if(showstat) chk_for_write();
}
morsebuf[inptr] = codeval;
inptr = tempptr;
return;
}
stopmorse()
{
fm_write(0xb0, beep_off_val); /* Turn off beep and */
inptr = outptr = 0; /* reset buffer pointers */
char_head = char_tail = 0;
delay_cntr = 0;
}
switch_on_noise()
{
int i;
set_voc_vol(0);
switch(noisetype) {
case 1: play_voc(sample[0], 0); break;
case 2: play_voc(sample[1], 0); break;
}
for(i = 0; i < noisevol+1; i++) {
set_voc_vol(i);
delay(40);
}
delay(1000);
}
switch_off_noise()
{
int i;
for(i = noisevol; i > -1; i--) {
set_voc_vol(i);
delay(40);
}
stop_voc();
}
switch_on_itone()
{
fm_write(0xb1, itone_on_val); /* Beep ON */
}
switch_off_itone()
{
fm_write(0xb1, itone_off_val);
}
select_characters()
{
int i, key, k;
unsigned char kstr[2], *ptr;
draw_chars();
while(1) {
if(bioskey(1)) {
key = bioskey(0);
k = key & 0xff;
if(k == 27) break;
switch(k) {
case 0:
k = (key >> 8) & 0xff;
switch(k){
case 38:
for(i=0;i<29;i++)
charstat[i] ^= 1;
break;
case 49:
for(i=29;i<39;i++)
charstat[i] ^= 1;
break;
case 45:
for(i=39;i<56;i++)
charstat[i] ^= 1;
break;
}
break;
default:
switch(k) {
case 134: /* � */
k = 143;
break;
case 132: /* � */
k = 142;
break;
case 148: /* � */
k = 153;
break;
default:
k = toupper(k);
}
charstat[strchr(letters, k) - letters] ^= 1;
}
draw_chars();
}
}
numchars = 0;
for(i = 0; i < 56; i++) {
if (charstat[i])
letters2[numchars++] = letters[i];
}
}
draw_chars()
{
int i;
gotoxy(2, 23);
for(i = 0; i < 56; i++) {
printf("%c", letters[i]);
if((i == 28) || (i == 38))
printf(" ");
}
printf("\n ");
for(i = 0; i < 56; i++) {
if(charstat[i])
printf("X");
else
printf(" ");
if((i == 28) || (i == 38))
printf(" ");
}
}
set_wpm()
{
get_option(2, 24, "New speed in WPM: ", "Invalid speed",
&wpm, 1, 999, 3, TRUE, FALSE);
set_wpm2();
}
set_wpm2()
{
float dit_msec;
unsigned int x_wpm = wpm, count, factor = 1;
if(x_wpm < 23) {
while(x_wpm < 23) {
x_wpm += wpm;
factor++;
}
}
dit_msec = 60000.0 / (float)x_wpm / 50.0;
count = (unsigned int)floor((dit_msec * 1193.181) + 0.5);
dit = 1 * factor;
dit_spacing = dit;
dah = dit * 3;
letter_spacing = dah - dit_spacing;
word_spacing = dit * 7 - letter_spacing;
set_timer_count(count);
}
set_tone_freq()
{
get_option(2, 24, "Frequency CW: ", "Invalid frequency",
&freq, 100, 9999, 4, TRUE, FALSE);
get_option(2, 24, "Frequency interference: ", "Invalid frequency",
&ifreq, 100, 9999, 4, TRUE, FALSE);
set_tone_freq2();
}
set_tone_freq2()
{
int block, fnum;
freq_to_fnum(freq, &block, &fnum);
fm_write(0xa0, fnum & 0xff);
beep_on_val = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
beep_off_val = beep_on_val & 0xdf;
freq_to_fnum(ifreq, &block, &fnum);
fm_write(0xa1, fnum & 0xff);
itone_on_val = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
itone_off_val = itone_on_val & 0xdf;
}
show_hide()
{
showstat ^= 1;
}
set_backg_noise()
{
char tmp[81];
sprintf(tmp, "Type of background noise (0=%s, 1=%s, 2=%s): ",
noisestr[0], noisestr[1], noisestr[2]);
get_option(2, 24, tmp, "Invalid type", &noisetype, 0, 2, 1,
TRUE, FALSE);
}
set_itone()
{
itonestat ^= 1;
}
set_volumes()
{
outportb(ct_io_addx + 4, 0x22);
mastervol = inportb(ct_io_addx+5) & 0xf;
outportb(ct_io_addx + 4, 0x26);
beepvol = inportb(ct_io_addx+5) & 0xf;
outportb(ct_io_addx + 4, 0x2E);
lineinvol = inportb(ct_io_addx+5) & 0xf;
get_option(2, 24, "Total: ", "Invalid volume",
&mastervol, 0, 15, 2, TRUE, FALSE);
get_option(2, 24, "Tones: ", "Invalid volume",
&beepvol, 0, 15, 2, TRUE, FALSE);
get_option(2, 24, "Background noise: ", "Invalid volume",
&noisevol, 0, 15, 2, TRUE, FALSE);
get_option(2, 24, "Line In: ", "Invalid volume",
&lineinvol, 0, 15, 2, TRUE, FALSE);
set_volume2();
}
set_volume2()
{
outportb(ct_io_addx + 4, 0x22);
outportb(ct_io_addx + 5, (mastervol << 4) + mastervol);
outportb(ct_io_addx + 4, 0x26);
outportb(ct_io_addx + 5, (beepvol << 4) + beepvol);
outportb(ct_io_addx + 4, 0x2e);
outportb(ct_io_addx + 5, (lineinvol << 4) + lineinvol);
}
set_voc_vol(int vol)
{
outportb(ct_io_addx + 4, 0x04);
outportb(ct_io_addx + 5, (vol << 4) + vol);
}
save_config()
{
printf_at(2, 24, "Save as: ");
getstr(cfgfname, 80, 0);
if(! strlen(cfgfname))
return;
chk_ext(cfgfname, "cfg");
printf_at(2, 24, "Saving configuration...");
cfgfile=fopen(cfgfname, "wb");
fwrite(&wpm, 2, 1, cfgfile);
fwrite(&charstat, 1, 56, cfgfile);
fwrite(&minlength, 2, 1, cfgfile);
fwrite(&maxlength, 2, 1, cfgfile);
fwrite(&mastervol, 2, 1, cfgfile);
fwrite(&beepvol, 2, 1, cfgfile);
fwrite(&noisevol, 2, 1, cfgfile);
fwrite(&noisetype, 2, 1, cfgfile);
fwrite(&lineinvol, 2, 1, cfgfile);
fwrite(&freq, 2, 1, cfgfile);
fwrite(&ifreq, 2, 1, cfgfile);
fwrite(&itonestat,2,1,cfgfile);
fclose(cfgfile);
delay(1000);
}
load_config()
{
char tmp[81];
int i;
printf_at(2, 24, "Configuration file to load: ");
getstr(cfgfname, 80, 0);
if(! strlen(cfgfname))
return;
chk_ext(cfgfname, "cfg");
if(! (cfgfile=fopen(cfgfname,"rb"))) {
sprintf(tmp, "Can't find %s!", cfgfname);
printf_at(2, 24, tmp);
delay(1000);
} else {
load_config2();
numchars = 0;
for(i=0;i<56;i++) {
if (charstat[i])
letters2[numchars++] = letters[i];
}
set_wpm2();
}
}
load_config2()
{
fread(&wpm, 2, 1, cfgfile);
fread(&charstat, 1, 56, cfgfile);
fread(&minlength, 2, 1, cfgfile);
fread(&maxlength, 2, 1, cfgfile);
fread(&mastervol, 2, 1, cfgfile);
fread(&beepvol, 2, 1, cfgfile);
fread(&noisevol, 2, 1, cfgfile);
fread(&noisetype, 2, 1, cfgfile);
fread(&lineinvol, 2, 1, cfgfile);
fread(&freq, 2, 1, cfgfile);
fread(&ifreq, 2, 1, cfgfile);
fread(&itonestat, 2, 1, cfgfile);
set_tone_freq2();
set_volume2();
fclose(cfgfile);
}
list_files()
{
textattr(0x07);
clrscr();
system("dir /w /p /on");
printf("\nPress any key to return to MORSE\n");
getch();
textattr(0x1e);
}
init_beep()
{
/* Beep tone */
fm_write(0x20, beep[0]); /* am/vib/ksr/multiple */
fm_write(0x23, beep[1]);
fm_write(0x40, beep[2]); /* ksl/total level */
fm_write(0x43, beep[3]);
fm_write(0x60, beep[4]); /* attack/decay rate */
fm_write(0x63, beep[5]);
fm_write(0x80, beep[6]); /* sustain level/release rate */
fm_write(0x83, beep[7]);
fm_write(0xe0, beep[8]); /* wave select */
fm_write(0xe3, beep[9]);
fm_write(0xc0, beep[10]); /* feedback/connection */
/* Interference tone */
fm_write(0x21, beep[0]); /* am/vib/ksr/multiple */
fm_write(0x24, beep[1]);
fm_write(0x41, beep[2]); /* ksl/total level */
fm_write(0x44, beep[3]);
fm_write(0x61, beep[4]); /* attack/decay rate */
fm_write(0x64, beep[5]);
fm_write(0x81, beep[6]); /* sustain level/release rate */
fm_write(0x84, beep[7]);
fm_write(0xe1, beep[8]); /* wave select */
fm_write(0xe4, beep[9]);
fm_write(0xc1, beep[10]); /* feedback/connection */
}
DosRead (int handle, char far *Buffer, unsigned wLen, unsigned *wByteRead)
{
union REGS regs;
struct SREGS segregs;
regs.h.ah = 0x3f ;
regs.x.bx = handle;
regs.x.dx = FP_OFF(Buffer);
regs.x.cx = wLen;
segregs.ds = FP_SEG(Buffer);
intdosx(®s, ®s, &segregs);
if(regs.x.cflag) /* error */
*wByteRead = 0;
else
*wByteRead = regs.x.ax ;
return(*wByteRead);
}
GetCurX()
{
asm{
push bx
push cx
mov ah,03h
mov ch,0
mov cl,24
mov bh,0
int 10h
mov al,dl
mov ah,0
pop cx
pop bx
}
}
printf_at(int x, int y, char *s)
{
gotoxy(x, y);
printf(s);
}
getstr(char *s, int maxlen, int mk_upper)
{
showcur();
edgets(s, maxlen, mk_upper);
hidecur();
gotoxy(2, 24);
printf(" ");
}
get_option(int x, int y, char *prompt, char *errtxt, int *val, int low_limit,
int high_limit, int nr_digits, int show_curr_val, int disp_add)
{
char s[6];
int val_add = 0, val_tmp;
if(disp_add)
val_add++;
while(1) {
gotoxy(x, y);
printf(prompt);
showcur();
if(show_curr_val)
sprintf(s, "%d", (*val) + val_add);
else
strcpy(s, "");
edgets(s, nr_digits, 1);
hidecur();
gotoxy(x, y);
if(! strlen(s))
return 0;
val_tmp = atoi(s) - val_add;
if((val_tmp >= low_limit) && (val_tmp <= high_limit)) {
(*val) = val_tmp;
gotoxy(x, y);
printf("%-78s", " ");
return 1;
}
gotoxy(x, y);
printf("%-78s", errtxt);
getch();
gotoxy(x, y);
printf("%-78s", " ");
}
}
freq_to_fnum (int freq, int *block, int *fnum)
{
static int f, octave;
f = freq;
octave = 5;
if (f == 0)
octave = 0;
else if (f < 261) {
while (f < 261) {
octave--;
f <<= 1;
}
} else if (f > 493) {
while (f > 493) {
octave++;
f >>= 1;
}
}
if (octave > 7)
octave = 7;
(*fnum) = freq * (1L << (20L - octave)) / 49716L;
(*block) = octave;
}
chk_for_write()
{
if(do_write_char) {
putchar(char_to_write);
do_write_char = FALSE;
}
}