#include <vectrex.h> #include <assert.h> #include "controller.h" static inline void set_scale(unsigned int scale) {/*VIA_t1_cnt_lo*/ *(volatile unsigned char *)0xD004 = scale;} typedef struct table { int ch; char *code; } table; // we will be printing the partially-received sequence on screen and it will // need a \0x80\0x00 termination, and we'll also be comparing that sequence // against the morse table, so might as well add the 0x80 here statically // rather than mess around with the string at runtime. table morse[] = { {'A',".-\x80"}, {'B',"-...\x80"}, {'C',"-.-.\x80"}, {'D',"-..\x80"}, {'E',".\x80"}, {'F',"..-.\x80"}, {'G',"--.\x80"}, {'H',"....\x80"}, {'I',"..\x80"}, {'J',".---\x80"}, {'K',"-.-\x80"}, {'L',".-..\x80"}, {'M',"--\x80"}, {'N',"-.\x80"}, {'O',"---\x80"}, {'P',".--.\x80"}, {'Q',"--.-\x80"}, {'R',".-.\x80"}, {'S',"...\x80"}, {'T',"-\x80"}, {'U',"..-\x80"}, {'V',"...-\x80"}, {'W',".--\x80"}, {'X',"-..-\x80"}, {'Y',"-.--\x80"}, {'Z',"--..\x80"}, {'0',"-----\x80"}, {'1',".----\x80"}, {'2',"..---\x80"}, {'3',"...--\x80"}, {'4',"....-\x80"}, {'5',".....\x80"}, {'6',"-....\x80"}, {'7',"--...\x80"}, {'8',"---..\x80"}, {'9',"----.\x80"}, {'.', ".-.-.-\x80"}, {',', "--..--\x80"}, {'?', "..--..\x80"}, {':', "---...\x80"}, {'+', ".-.-.\x80"}, {'-', "-....-\x80"}, {'/', "-..-.\x80"}, {'=', "-...-\x80"}, {'\'', ".----.\x80"}, {'(', "-.--.\x80"}, {')', "-.--.-\x80"}, {'_', "..--.-\x80"}, {'!', "-.-.--\x80"}, {'&', ".-...\x80"}, {'"', ".-..-.\x80"}, {';', "-.-.-.\x80"}, {'$', "...-..-\x80"}, {':', "---...\x80"}, {'@', ".--.-.\x80"}, #define EM_DASH (-1) {-1, "-...-\x80"}, /* '<em-dash:-->' */ {-2, "...-.\x80"}, /* '<understood:u>' */ #define ERROR (-3) {-3, "........\x80"}, /* '<error:e>' */ {-4, "-.-\x80"}, /* '<invitation to transmit:k>' */ {-5, ".-...\x80"}, /* '<wait:w>' */ {-6, "...-.-\x80"}, /* '<end of work:]>' */ {-7, "-.-.-\x80"}, /* '<start of transmission:[>' */ {-8, "-.-...\x80"}, /* '<end of paragraph:\n>' */ #define SOS (-9) {-9, "...---...\x80"}, /* '<SOS>' */ {0, 0} }; #define MAX_LINES 9 #define LETTERS_PER_LINE 20 #define MAX_CHARS (1+LETTERS_PER_LINE+1+1) static char line[MAX_LINES][MAX_CHARS]; int strcmp(char *a, char *b) { for (;;) { if (*a == *b) { if (*a == 0) return 0; } else return (int)(*a - *b); a++; b++; } } // there's a lack of checking of buffer lengths throughout this code. // not really urgent. Will fix later. #define LONGEST_SEQUENCE 30 // overkill static char letter[1+LONGEST_SEQUENCE+1+1] = { 0x80, 0 }; // for the morse representation of one letter, being decoded. char *letterp = letter; int DECODE_LAST_LETTER(void) { // This is an expensive loop and could be optimised considerably by using a tree, // however at the moment it is fast enough to perform on every clock tick. int i = 0; for (;;) { if (morse[i].ch == 0) return 0; if (strcmp(letter, morse[i].code) == 0) return morse[i].ch; i += 1; } } void start_800hz_tone() { } void silence_800hz_tone() { } #define BASE 50 // increase this to move the text lower down the screen int main(void) { /* The length of a dot is 1 time unit. A dash is 3 time units. The space between symbols (dots and dashes) of the same letter is 1 time unit. The space between letters is 3 time units. The space between words is 7 time units. */ #define DOT_DURATION 2 #define DASH_DURATION 6 #define SYMBOL_GAP 2 #define LETTER_GAP 18U #define WORD_GAP 80U // 42U but stretched for beginners #define NEWLINE_GAP 250U int i,j; unsigned int ticks = 0U; char *xword = &line[0][0]; // for the reconstructed ascii word. #define word (xword+1) char *wordp; letterp = letter; letter[0] = 0x80; letter[1] = 0; xword[0] = ' '; xword[1] = ' '; wordp = xword+1; xword[2] = 0x80; xword[3] = 0; for (i = 0; i < MAX_LINES; i++) { line[i][0] = ' '; // Print_Str_d needs a string of >= 2 characters to work properly! line[i][1] = ' '; line[i][2] = 0x80; line[i][3] = 0; } // state machine: #define WAITING_FOR_KEY_DOWN 1 #define COUNTING_GAP 2 #define WAITING_FOR_KEY_UP 3 int state = WAITING_FOR_KEY_DOWN; for (;;) { Wait_Recal(); check_buttons(); set_scale(0x7F); // echo current letter being entered: Reset0Ref(); Intensity_3F(); Moveto_d(-24-BASE, -120); Draw_Line_d(0,20);Draw_Line_d(6,-10);Moveto_d(-6,10);Draw_Line_d(-6,-10); Intensity_7F(); if (letter[0] != 0x80) { i = 0;; Reset0Ref(); Moveto_d(-24-BASE, -90); while (letter[i] != 0x80) { Draw_Line_d(0, (letter[i] == '.' ? 2 : 6)); Moveto_d(0, 6); i += 1; } } for (i = 0; i < MAX_LINES; i++) { if (line[i][1] != ' ' || line[i][2] != 0x80) { Reset0Ref(); Intensity_5F(); Print_Str_d((i<<4)-BASE, -108, &line[i][0]); } } switch (state) { case WAITING_FOR_KEY_UP: if (button_1_4_held()) break; // keep waiting while key is pressed. Ticks will increase. silence_800hz_tone(); // key was just now released. How long was it? *letterp++ = (char)(ticks <= DASH_DURATION ? '.' : '-'); *letterp = 0x80; letterp[1] = 0; ticks = 0L; state = COUNTING_GAP; break; case COUNTING_GAP: if (ticks == LETTER_GAP) { // end of letter gap. Could be longer. Or letter could be empty. int ch = DECODE_LAST_LETTER(); if (ch > 0) { *wordp++ = (char)ch; wordp[0] = 0x80; wordp[1] = 0; } else if (ch == ERROR) { while ((wordp > word) && (wordp[-1] == ' ')) wordp -= 1; if (wordp > word) { // ........ means erase last character received. wordp -= 1; wordp[0] = 0x80; wordp[1] = 0; while ((wordp > word) && (wordp[-1] == ' ')) wordp -= 1; } } else if (ch == EM_DASH) { *wordp++ = (char)'-'; *wordp++ = (char)'-'; wordp[0] = 0x80; wordp[1] = 0; } else if (ch == SOS) { // Sent as a group with no gaps *wordp++ = (char)'S'; *wordp++ = (char)'O'; *wordp++ = (char)'S'; wordp[0] = 0x80; wordp[1] = 0; } letterp = letter; letter[0] = 0x80; letter[1] = 0; } else if (ticks == WORD_GAP) { // word gap - extra long for beginners. Letter has already been saved. if ((wordp != word) && (wordp[-1] != ' ')) { *wordp++ = ' '; wordp[0] = 0x80; wordp[1] = 0; } } else if ((ticks == NEWLINE_GAP) && (wordp != word)) { // not a standard morse convention char *to = &line[0][0]; wordp = xword; for (;;) { char c = *wordp++; *to++ = c; if (c == 0) break; } // scroll for (i = MAX_LINES-1; i > 0; i--) { for (j = 0; j < MAX_CHARS; j++) { line[i][j] = line[i-1][j]; } } xword[0] = ' '; xword[1] = ' '; xword[2] = 0x80; xword[3] = 0; wordp = word; } else if (ticks == 255U) { ticks = 254U; // maxed out. Will stay at 255 through every loop until key pressed again. } // fall through... case WAITING_FOR_KEY_DOWN: if (button_1_4_pressed()) { ticks = 0U; start_800hz_tone(); state = WAITING_FOR_KEY_UP; } break; } ticks += 1U; // counting length of 'off' sequence. } return 0; }