/******************************************************/
/*  MORSE v1.20                                       */
/*  CW trainer for Soundblaster                       */
/******************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <bios.h>
#include <dir.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 <w1.h>
#include <windprot.h>
#include <color.h>
#include <sb.h>
#include <edgets.h>
#include <bk_delay.h>
#include "morseisr.h"

/* #pragma inline; */

#define MORSE_VERSION "1.20"

#define MORSEBUFSIZE 128   /* Number of entries in morse data buffer */

#define ONOFF           0x8000  /* Top bit controls tone on or off */
#define CHAR_COMPLETED  0x4000

#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"};
int mastervol,beepvol,noisevol,lineinvol;
int wpm;
int dah, dit, dit_spacing, letter_spacing, word_spacing;
int noisetype;

char txtfname[81];

unsigned char far *dma_buf;

int numsamples;
unsigned char far *sample[10], far *quit_sample;
unsigned long sampsize[10], quit_sampsize;

unsigned char letters[] = {
       "ABCDEFGHIJKLMNOPQRSTUVWXYZ���0123456789.,?!:\"'()/-*=~�+@"
};

unsigned char used_letters[56];
unsigned char letter_stat[56];
int num_used;

unsigned int freq, ifreq;
unsigned char beep_on_val, beep_off_val, itone_on_val, itone_off_val;
unsigned int attack, release;

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 void fm_write(unsigned char reg,unsigned char data);


FILE* cfgfile;
char cfgfname[81];

unsigned char char_to_write = 0;

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 't': set_at_rel(); 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 'j': jump_to_dos(); break;
                       case 'q': quit();
               }
               draw_menu();
       }
}


init()
{
       int i, handle;
       FILE* f;
       unsigned int wavsize;
       unsigned numread, segp;
       struct WAVhdr wav_hdr;

       sb_init(0x220, 5, 1);
       sb_set_samprate(11025);
       dsp_voice(1);

       strcpy(cfgfname, "MORSE.CFG");
       if(cfgfile = fopen(cfgfname,"rb"))
               load_config2();
       else {
               wpm = 20;
               for(i = 0; i < 56; i++)
                       letter_stat[i] = 1;

               minlength=2;
               maxlength=5;
               mastervol=12;
               beepvol=15;
               noisevol=8;
               noisetype=1;
               itonestat=0;
               freq = 700;
               ifreq = 720;
               attack = 12;
               release = 12;
       }

       num_used = 0;

       for(i = 0; i < 56; i++) {
               if (letter_stat[i])
                       used_letters[num_used++] = letters[i];
       }

       randomize();

       dma_buf = aligned_malloc(65536L);

       if((handle = _open("MORSE.DAT", O_RDONLY)) == -1) {
               printf("Error opening MORSE.DAT\n");
               sb_exit();
               exit(1);
       }

       numsamples = 0;
       while(1) {
               DosRead(handle, (char far *)&wavsize, 2, &numread);
               if(! numread) break;
               DosRead(handle, (struct WAVhdr far*)&wav_hdr, 44, &numread);
               allocmem((wav_hdr.data_length + 15L) >> 4, &segp);
               sample[numsamples] = MK_FP(segp, 0);
               DosRead(handle, sample[numsamples], wav_hdr.data_length, &numread);
               sampsize[numsamples] = wav_hdr.data_length - 1;
               lseek(handle, wavsize - (44 + wav_hdr.data_length), SEEK_CUR);
               numsamples++;
       }
       close(handle);

       if((handle = _open("QRT.WAV", O_RDONLY)) == -1) {
               printf("Error opening QRT.WAV\n");
               sb_exit();
               exit(1);
       }
       DosRead(handle, (struct WAVhdr far*)&wav_hdr, 44, &numread);
       allocmem((wav_hdr.data_length + 15L) >> 4, &segp);
       quit_sample = MK_FP(segp, 0);
       DosRead(handle, quit_sample, wav_hdr.data_length, &numread);
       quit_sampsize = wav_hdr.data_length - 1;
       close(handle);

       inst_morse_isr();
       init_beep();
       set_at_rel2();
       set_tone_freq2();
       set_volume2();

       qinit();
       textattr(0x1e);
       hidecur();
       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()) {
               sb_set_volume(MIXER_VOICE, 0xff);
               _fmemcpy(dma_buf, quit_sample, quit_sampsize);
               sb_play(dma_buf, quit_sampsize - 1, quit_sampsize - 1, 0);
               while(! block_done);

               for(i = 0; i < numsamples; i++)
                       freemem(FP_SEG(sample[i]));

               rest_morse_isr();
               sb_exit();

               textattr(0x07);
               qfill(1, 1, 25, 80, 0x07, ' ');
               gotorc(1, 1);
               showcur();
               exit(0);
       }
}


draw_menu()
{
       char tmp[82];

       vsync();
       qfill(1, 1, 25, 80, 0x1e, ' ');

       qwrite(2, 2,  0x1e, "������������������������������������������Ŀ");

       sprintf(tmp,        "�  MORSE v%s   by Mats Petersson SM5SXL  �", MORSE_VERSION);
       qwrite(3, 2,  0x1e, tmp);
       qwrite(4, 2,  0x1e, "��������������������������������������������");

       qwrite(6, 2,  0x1e, "1  Groups of 5 with random characters");
       qwrite(7, 2,  0x1e, "2  Variable length groups with random characters");
       qwrite(8, 2,  0x1e, "3  Text from file");
       qwrite(9, 2, 0x1e,  "C  Select characters");
       sprintf(tmp,        "M  Set min/max length              [Min %d  Max %d]", minlength, maxlength);
       qwrite(10, 2, 0x1e, tmp);
       sprintf(tmp,        "W  Set speed in WPM                [%d]", wpm);
       qwrite(11, 2, 0x1e, tmp);
       sprintf(tmp,        "F  Set tone frequencies            [CW %d  Interference %d]", freq, ifreq);
       qwrite(12, 2, 0x1e, tmp);
       sprintf(tmp,        "T  Set CW tone attack/release      [Attack %d  Release %d]", attack, release);
       qwrite(13, 2, 0x1e, tmp);
       sprintf(tmp,        "I  Interference tone on/off        [%s]",itonestr[itonestat]);
       qwrite(14, 2, 0x1e, tmp);
       sprintf(tmp,        "H  Show/hide characters            [%s]",shwstatstr[showstat]);
       qwrite(15, 2, 0x1e, tmp);
       sprintf(tmp,        "B  Set background noise sample     [%d]", noisetype);
       qwrite(16, 2, 0x1e, tmp);
       sprintf(tmp,        "V  Set volume levels               [Total %u  Tones %u  BG %u  Line-In %u]",
                                mastervol, beepvol, noisevol, lineinvol);
       qwrite(17, 2, 0x1e, tmp);
       qwrite(18, 2, 0x1e, "S  Save configuration");
       qwrite(19, 2, 0x1e, "L  Load configuration");
       qwrite(20, 2, 0x1e, "D  Dir (List files)");
       qwrite(21, 2, 0x1e, "J  Jump to DOS");
       qwrite(22, 2, 0x1e, "Q  Quit");
}


set_minmax()
{
       int old_minlength, old_maxlength;

       old_minlength = minlength;
       get_option(2, 24, "Min length: ", "Invalid length",
       &minlength, 1, 80, 2, TRUE, FALSE);

       if(maxlength < minlength)
               maxlength = minlength;
       old_maxlength = maxlength;

       while(1) {
               get_option(2, 24, "Max length: ", "Invalid length",
               &maxlength, 1, 80, 2, TRUE, FALSE);
               if(maxlength < minlength) {
                       gotoxy(2, 24);
                       printf("Max length cannot be smaller than Min length!\n");
                       bk_delay(1500);
                       gotoxy(2, 24);
                       printf("%-78s", " ");
                       maxlength = old_maxlength;
               } else
                       break;
       }
}


random_text(int mode)
{
       unsigned char rndstring[82];
       unsigned int i, col = 1;
       int strlength, linelength = 0, old_linelength, first = TRUE;

       textattr(0x07);
       clrscr();
       delay(500);

       if(noisetype)
               switch_on_noise();
       if(itonestat)
               switch_on_itone();


       while(! esc_key()) {
               if(mode == 0)
                       strlength = 5;
               else
                       strlength = random(maxlength-(minlength-1)) +
                                    minlength;

               for(i = 0; i < strlength; i++)
                       rndstring[i] = used_letters[random(num_used)];

               rndstring[strlength] = 0;

               if(first) {
                       linelength += strlength;
                       first = FALSE;
               } else {
                       old_linelength = linelength;
                       linelength += (strlength + 1);
                       if(linelength > 80) {
                               if(old_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);
               bk_delay(1500);
               return;
       }

       textattr(0x07);
       clrscr();
       if(noisetype)
               switch_on_noise();
       if(itonestat)
               switch_on_itone();

       while((! esc_key()) && fgets(filestr,80,f))
               out_str(filestr);

       fclose(f);

       while((! esc_key()) && (inptr != outptr)) /* Wait for buffer to empty */
               if(showstat) chk_for_write();

       if(esc_key()) getch();
       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(esc_key()) return;
               if(showstat) chk_for_write();
       }
}


out_char(unsigned int ascii_char) {
       unsigned char ch, *cp;
       unsigned int i, was_char, val, len, last_pos;

       cp = morsecode[ascii_char];
       if((*cp) == 0) return;

       was_char = FALSE;
       len = strlen(cp);
       last_pos = len - 1;

       for(i = 0; i < len; i++) {
               switch(*(cp + i)) {
                       case 'l' :
                               val = letter_spacing;
                               break;
                       case 'w' :
                               val = word_spacing;
                               break;
                       case '.' :
                               putmorse(dit | ONOFF);
                               val = dit_spacing;
                               was_char = TRUE;
                               break;
                       case '-' :
                       case '_' :
                               putmorse(dah | ONOFF);
                               val = dit_spacing;
                               was_char = TRUE;
                               break;
               }

               if(i == last_pos)
                       val |= CHAR_COMPLETED | (ascii_char << 5);
               putmorse(val);
       }

       if (was_char)
            putmorse(letter_spacing);
}


putmorse(unsigned int codeval)
{
       unsigned int tempptr;

       if(esc_key()) return;
       if(showstat) chk_for_write();

       tempptr = (inptr + 1) & (MORSEBUFSIZE - 1);

       while (outptr == tempptr) {          /* Wait for space in buffer */
               if(esc_key()) 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 */
       delay_cntr = 0;
}


switch_on_noise()
{
       int i;
       unsigned int len;
       unsigned char far *smp;

       set_voc_vol(0);

       smp = sample[noisetype - 1];
       len = sampsize[noisetype - 1];
       _fmemcpy(dma_buf, smp, len);
       sb_play(dma_buf, len - 1, 4095, 1);

       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);
       }
       sb_stop();
}

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) {
               key = bioskey(0);
               k = key & 0xff;
               if(k == 27) break;

               if(k == 0) {
                       k = (key >> 8) & 0xff;

                       switch(k) {
                               case 38:        /* Alt-L */
                                       for(i=0;i<29;i++)
                                               letter_stat[i] ^= 1;
                                       break;
                               case 49:        /* Alt-N */
                                       for(i=29;i<39;i++)
                                               letter_stat[i] ^= 1;
                                       break;
                               case 45:        /* Alt-X */
                                       for(i=39;i<56;i++)
                                               letter_stat[i] ^= 1;
                                       break;
                       }
               } else {
                       switch(k) {
                               case 134:  /* � */
                                       k = 143;
                                       break;
                               case 132:  /* � */
                                       k = 142;
                                       break;
                               case 148:  /* � */
                                       k = 153;
                                       break;
                               default:
                                       k = toupper(k);
                       }
                       letter_stat[strchr(letters, k) - letters] ^= 1;
               }
               draw_chars();
       }

       num_used = 0;
       for(i = 0; i < 56; i++) {
               if (letter_stat[i])
                       used_letters[num_used++] = letters[i];
       }
}


draw_chars()
{
       char tmp[82], tmp2[3];
       int i;

       strcpy(tmp, "");
       for(i = 0; i < 56; i++) {
               sprintf(tmp2, "%c", letters[i]);
               strcat(tmp, tmp2);
               if((i == 28) || (i == 38))
                       strcat(tmp, "  ");
       }
       qwrite(24, 2, 0x1e, tmp);

       strcpy(tmp, "");
       for(i = 0; i < 56; i++) {
               if(letter_stat[i])
                       strcat(tmp, "�");
               else
                       strcat(tmp, " ");

               if((i == 28) || (i == 38))
                       strcat(tmp, "  ");
       }
       qwrite(25, 2, 0x1e, tmp);
}


set_wpm()
{
       get_option(2, 24, "New speed in WPM: ", "Invalid speed",
       &wpm, 5, 999, 3, TRUE, FALSE);
       set_wpm2();
}


set_wpm2()
{
       float dit_msec;
       unsigned int x_wpm = wpm, count, dit_adjust = 1;

       if(x_wpm < 23) {                /* 23 WPM = approximate limit for */
               while(x_wpm < 23) {     /* timer count to be within 65535 */
                       x_wpm += wpm;   /* If below 23 WPM, then multiply */
                       dit_adjust++;   /* it enough times to be above,   */
               }                       /* and adjust the number of ticks */
       }                               /* for a 'dit' accordingly        */

       dit_msec = 60000.0 / (float)x_wpm / 50.0;
       count = (unsigned int)floor((dit_msec * 1193.181) + 0.5);

       dit = 1 * dit_adjust;
       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, 10, 6213, 4, TRUE, FALSE);

       get_option(2, 24, "Frequency interference: ", "Invalid frequency",
       &ifreq, 10, 6213, 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;
}


set_at_rel()
{
       get_option(2, 24, "Attack: ", "Invalid attack value",
       &attack, 0, 15, 4, TRUE, FALSE);

       get_option(2, 24, "Release: ", "Invalid release value",
       &release, 0, 15, 4, TRUE, FALSE);

       set_at_rel2();
}

set_at_rel2()
{
       fm_write(0x60, (beep[4] & 0x0f) | (attack << 4));  /* attack/decay rate */
       fm_write(0x63, (beep[5] & 0x0f) | (attack << 4));
       fm_write(0x80, (beep[6] & 0xf0) | release);  /* sustain level/release rate */
       fm_write(0x83, (beep[7] & 0xf0) | release);
}


show_hide()
{
       showstat ^= 1;
}


set_backg_noise()
{
       char tmp[81];

       sprintf(tmp, "Sample to use as background noise (1 - %d, or 0 for none): ",
               numsamples);

       get_option(2, 24, tmp, "Value out of range", &noisetype, 0, numsamples, 1,
       TRUE, FALSE);
}


set_itone()
{
       itonestat ^= 1;
}


set_volumes()
{
       get_vols();

       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()
{
       sb_set_volume(MIXER_MASTER, (mastervol << 4) + mastervol);
       sb_set_volume(MIXER_FM, (beepvol << 4) + beepvol);
       sb_set_volume(MIXER_LINE, (lineinvol << 4) + lineinvol);
       get_vols();
}


set_voc_vol(int vol)
{
       sb_set_volume(MIXER_VOICE, (vol << 4) + vol);
}


get_vols()
{
       mastervol = sb_get_volume(MIXER_MASTER) & 0x0f;
       beepvol = sb_get_volume(MIXER_FM) & 0x0f;
       lineinvol = sb_get_volume(MIXER_LINE) & 0x0f;
}


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(&letter_stat, 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(&attack, 2, 1, cfgfile);
       fwrite(&release, 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);
               bk_delay(1500);
       } else {
               load_config2();
               num_used = 0;
               for(i=0;i<56;i++) {
                       if (letter_stat[i])
                               used_letters[num_used++] = letters[i];
               }
               set_wpm2();
       }
}

load_config2()
{
       fread(&wpm, 2, 1, cfgfile);
       fread(&letter_stat, 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(&attack, 2, 1, cfgfile);
       fread(&release, 2, 1, cfgfile);
       fread(&itonestat, 2, 1, cfgfile);

       set_tone_freq2();
       set_volume2();

       fclose(cfgfile);
}


list_files()
{
       textattr(0x07);
       qfill(1, 1, 25, 80, 0x07, ' ');
       gotorc(1, 1);
       system("dir /w /p /on");
       printf("\nPress any key to return to MORSE\n");
       getch();
       textattr(0x1e);
}


jump_to_dos()
{
       char s[82], *oldprompt;

       textattr(0x07);
       qfill(1, 1, 25, 80, 0x07, ' ');
       gotorc(1, 1);
       showcur();
       oldprompt = getenv("PROMPT");
       putenv("PROMPT=$_Type EXIT to return to MORSE$_$p$g");
       system("command");
       strcpy(s, "PROMPT=");
       strcat(s, oldprompt);
       putenv(s);                      /* restore old prompt */
       hidecur();
       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(&regs, &regs, &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);
               bk_delay(1500);
               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(char_to_write) {
               putchar(char_to_write);
               char_to_write = 0;
       }
}


esc_key()
{
       int k = bioskey(1);

       if(k && ((k & 0xff) != 27)) {
               bioskey(0);
               k = 0;
       }
       return k;
}