#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;
}