/* ** <<< Morse Code Functions >>> ** ** Written by Michael M. Dodd, N4CF, and placed in the public domain. ** ** The morse() function transmits a string in Morse code on the IBM PC's ** speaker. The speed is set by a program constant (UNIT_TIME). ** ** There are several other functions in this file, all used by morse(), ** and defined ahead of morse() for convenience. ** ** The main() function at the end of the file is a test program to send ** the command-line argument string in morse code. Enclose multiple ** words in quotes. Example: morse "hello, world" ** ** These functions have been compiled and tested in the Small and Large ** memory models using Microsoft C 6.00a. ** ** Modified for ZTC++, TC++, & BC++ by Bob Stout */ #include #include #include #include /* ** These functions turn on and off the CW tone on the PC's speaker. The ** frequency is specified by the freq argument. ** IMPORTANT! These functions are highly IBM PC-specific! */ #define CLK_FREQ (1193180L) #define PIO (0x61) #define CTC_CMD (0x43) #define CTC_DATA (0x42) #define SETUP (0xB6) #define TONE_ON (0x03) #define TONE_OFF (0xFC) void note_on (int freq) /* Turn on the tone. */ { int divisor ; int pio_word ; divisor = (int)(CLK_FREQ / (long)(freq)) ; outp (CTC_CMD, SETUP) ; outp (CTC_DATA, divisor & 0xFF) ; outp (CTC_DATA, divisor >> 8) ; pio_word = inp (PIO) ; outp (PIO, pio_word | TONE_ON) ; } void note_off (void) /* Turn off the tone. */ { int pio_word ; pio_word = inp (PIO) ; outp (PIO, pio_word & TONE_OFF) ; } /* ** These functions implement a timing-loop delay. Because the PC's ** internal clock is too coarse for accurate CW timing, the pause() ** function uses a simple software loop to produce a delay. ** ** To minimize the effects of CPU clock speed, the calib() function ** returns a number which represents a rough index of the clock speed ** with relation to the standard IBM PC (this is very approximate). ** ** Calibration is performed only once, when the static fudge_factor is ** zero. Thereafter, the contents of fudge_factor are used to form a ** delay value. ** ** IMPORTANT! These functions are highly IBM PC-specific! */ unsigned int calib (void) { unsigned int far *timerLow = (unsigned int far *)(0x046c) ; unsigned int lastTime ; unsigned int iter ; for (lastTime = *timerLow; lastTime == *timerLow;) ; for (iter = 0, lastTime = *timerLow; lastTime == *timerLow; iter++) ; #if defined(__ZTC__) return ((unsigned int)((125L * ((long)(iter)) + 50L) / 2300L)) ; #elif defined(__TURBOC__) return ((unsigned int)((77L * ((long)(iter)) + 50L) / 2300L)) ; #else /* assume MSC */ return ((unsigned int)((100L * ((long)(iter)) + 50L) / 2300L)) ; #endif } void pause (unsigned int amount) { static unsigned int fudge_factor = 0 ; unsigned long ul ; if (fudge_factor == 0) /* Calibrate the speed. */ fudge_factor = calib () ; ul = (unsigned long)(amount) * (long)(fudge_factor) ; while (ul--) /* Delay. */ ; } /* ** These functions transmit a dot, a dash, a letter space, and a ** word space. ** ** Note that a single unit space is automatically transmitted after ** each dot or dash, so the ltr_space() function produces only a ** two-unit pause. ** ** Also, the word_space() function produces only a four-unit pause ** because the three-unit letter space has already occurred following ** the previous letter. */ #define SPACE_MASK (1 << 15) #define BIT_MASK (0xfe) #define UNIT_TIME (18) #define FREQUENCY (1500) void send_dot (void) /* Send a dot and a space. */ { note_on (FREQUENCY) ; pause (UNIT_TIME) ; note_off () ; pause (UNIT_TIME) ; } void send_dash (void) /* Send a dash and a space. */ { note_on (FREQUENCY) ; pause (UNIT_TIME * 3) ; note_off () ; pause (UNIT_TIME) ; } void ltr_space (void) /* Produce a letter space. */ { pause (UNIT_TIME * 2) ; } void word_space (void) /* Produce a word space. */ { pause (UNIT_TIME * 4) ; } /* ** MORSE () - Transmit a string in Morse code ** ** This function transmits the string pointed to by the cp argument in ** Morse code on the PC's speaker. The speed is set by the UNIT_TIME ** constant. ** ** A static table translates from ASCII to Morse code. Each entry is ** an unsigned integer, where a zero represents a dot and a one ** represents a dash. No more than 14 bits may be used. Setting bit ** 15 produces a word space, regardless of any other pattern. ** ** The Morse code pattern is taken from bit 0, and is shifted right ** each time an element is sent. A special "marker bit" follows the ** complete Morse pattern. This marker bit is tested before ** transmitting each bit; if there are no 1's in bits 1..15, the ** complete character has been sent. ** ** For example, an "L" would be 0000000000010010, with bit zero ** containing the first dot, bit one the dash, etc. The marker ** bit is in bit 4. */ void morse (char *cp) { /*--- MORSE CODE FUNCTION ---*/ unsigned int c ; static unsigned int codes [64] = { SPACE_MASK, /* Entry 0 = space (0x20) */ 0, 0, 0, 0, 0, 0, 0, 0, /* ! " # $ % & " ( */ 0, 0, 0, 115, 49, 106, 41, /* ) * + , - . / */ 63, 62, 60, 56, 48, 32, 33, 35, /* 0 1 2 3 4 5 6 7 */ 39, 47, 0, 0, 0, 0, 0, 76, /* 8 9 : ; < = > ? */ 0, 6, 17, 21, 9, 2, 20, 11, /* @ A B C D E F G */ 16, 4, 30, 13, 18, 7, 5, 15, /* H I J K L M N O */ 22, 27, 10, 8, 3, 12, 24, 14, /* P Q R S T U V W */ 25, 29, 19 /* X Y Z */ } ; pause (0) ; /* Calibrate pause() function. */ while ((c = *cp++) != '\0') { /*--- TRANSMIT COMPLETE STRING ---*/ c = toupper (c) ; /* No lower-case Morse characters. */ c -= ' ' ; /* Adjust for zero-based table. */ if (c < 0 || c > 58) /* If out of range, ignore it. */ continue ; c = codes[c] ; /* Look up Morse pattern from table. */ if (c & SPACE_MASK) /* If the space bit is set.. */ { /* ..send a word space and go on. */ word_space () ; continue ; } while (c & BIT_MASK) /* Transmit one character. */ { /*--- TRANSMIT EACH BIT ---*/ if (c & 1) send_dash () ; else send_dot () ; c >>= 1 ; } /*--- TRANSMIT EACH BIT ---*/ ltr_space () ; /* Send a space following character. */ } /*--- TRANSMIT COMPLETE STRING ---*/ } /*--- MORSE CODE FUNCTION ---*/ /* ** This is the test program, which transmits argv[1] in Morse code. */ void main (int argc, char *argv[]) { if (argc > 1) morse (argv[1]) ; }