REM REM cwtext.bas REM REM manfred haertel 01.10.1995 - 16.06.1996 REM REM displays a text on the screen and asks the users to give the appropriate REM morse signals REM REM this program is still relatively quick and dirty. comments are welcome! REM REM you may freely redistribute this program, change it for your own porpuses REM or use parts of it in your programs, as long as you include a reference REM to the original author (me) REM REM mailto:Manfred.Haertel@t-online.de REM http://home.t-online.de/home/Manfred.Haertel REM start: ON ERROR GOTO genericerror CLEAR REM REM names for colors REM yellow = 14 white = 15 green = 2 red = 4 black = 0 wrongchars$ = "" REM REM other definitions REM backspace = 8 maxrow = 23 maxcol = 80 maxcode = 3500 maxmorse = 128 dummylooplength = 50 REM REM initialize counters REM correctchars = 0 errors = 0 REM REM set the graphics mode we need and clear the screen SCREEN 12 CLS REM REM announce REM PRINT "CWTEXT.BAS" PRINT "Morse-Lernprogramm" PRINT "(C) 1995 Manfred H„rtel" PRINT "" PRINT "Um dieses Programm benutzen zu k”nnen, mssen Sie eine Morsetaste" PRINT "an einen seriellen Port Ihres Rechners so angeschlossen haben, daá" PRINT "bei gedrckter Morsetaste die CTS- und RTS-Leitungen" PRINT "kurzgeschlossen werden." PRINT "" PRINT "Sie geben einen Dateinamen ein. Das Programm zeigt diese Datei" PRINT "auf dem Bildschirm an. Sie mssen das weiss angezeigte Zeichen" PRINT "morsen. Richtig gegebene Zeichen werden grn, falsche rot." PRINT "Nach falsch gegebenen Zeichen mssen Sie zuerst eine Irrung geben." PRINT "" PRINT "Wenn Sie ein Zeichen nicht mehr wissen, so drcken Sie eine Taste" PRINT "an der Tastatur. Dann gibt der Computer das Zeichen in der aktuellen" PRINT "Geschwindigkeit." PRINT "" BEEP REM REM ask for com device, using the old one as a default REM selectcom: ON ERROR GOTO comininotfound OPEN "com.ini" FOR INPUT AS #2 ON ERROR GOTO genericerror LINE INPUT #2, oldcomdevice$ CLOSE #2 cominput: PRINT "Bitte geben Sie die serielle Schnittstelle ein, an der die" PRINT "Morsetaste h„ngt. Wenn Sie nur RETURN drcken, wird der angegebene" PRINT "Default genommen." PRINT "" PRINT "Serielle Schnittstelle ["; oldcomdevice$; "]"; INPUT comdevice$ comdevice$ = UCASE$(comdevice$) IF comdevice$ = "" THEN comdevice$ = oldcomdevice$ REM REM set io base address according to com device REM IF comdevice$ = "COM1:" THEN iobase = &H3F8 ELSEIF comdevice$ = "COM2:" THEN iobase = &H2F8 ELSEIF comdevice$ = "COM3:" THEN iobase = &H3E8 ELSEIF comdevice$ = "COM4:" THEN iobase = &H2E8 ELSE PRINT "Sie haben eine ungltige Schnittstelle angegeben!" GOTO selectcom END IF REM REM save new com device if needed REM IF comdevice$ <> oldcomdevice$ THEN OPEN "com.ini" FOR OUTPUT AS #3 PRINT #3, comdevice$ CLOSE #3 END IF REM REM ask if we want a frequency change if line too long REM REM REM read ini file for old status REM ON ERROR GOTO warnininotfound OPEN "warn.ini" FOR INPUT AS #6 ON ERROR GOTO genericerror INPUT #6, oldwarn CLOSE #6 REM REM ask for possible new input and interpret it REM warninput: IF oldwarn = 1 THEN defaultwarn$ = "J" ELSE defaultwarn$ = "N" END IF PRINT "" PRINT "Warnung bei zu langen Strichen (j/n) ["; defaultwarn$; "]"; INPUT warn$ warn$ = UCASE$(warn$) IF warn$ = "" THEN REM REM empty input, no changes REM warniflinetoolong = oldwarn ELSE REM REM explicit yes or no REM IF warn$ = "J" THEN warniflinetoolong = 1 ELSEIF warn$ = "N" THEN warniflinetoolong = 0 ELSE REM REM if not yes or no, ask again REM GOTO warninput END IF REM REM save new status REM OPEN "warn.ini" FOR OUTPUT AS #7 PRINT #7, warniflinetoolong CLOSE #7 END IF gotwarn: REM REM initialize morse decoder REM GOSUB initmorse REM REM inquire filename of text file REM getfilename: PRINT "" INPUT "Name der Text-Datei: ", textfile$ ON ERROR GOTO filenotfound OPEN textfile$ FOR INPUT AS #1 ON ERROR GOTO genericerror REM REM open file in which we save the durations for later graphical display REM OPEN "cw.log" FOR OUTPUT AS #8 REM REM write dummy pause to the log REM PRINT #8, "+ 1" REM REM display text REM COLOR yellow CLS REM REM we reserve the first two lines for the display of the morse signs REM LOCATE 3, 1 FOR recordnumber = 3 TO maxrow LINE INPUT #1, record$ PRINT record$ IF EOF(1) THEN GOTO outputdone NEXT recordnumber outputdone: CLOSE #1 REM REM get start time REM starttime = TIMER REM REM now loop over all rows and columns and ask for the text REM FOR row = 3 TO maxrow FOR column = 1 TO maxcol REM REM get character on the screen REM character = SCREEN(row, column) REM REM blanks are ignored!!! REM IF character <> ASC(" ") THEN REM REM display the morse signal REM GOSUB displaymorse REM REM loop until this character is given correctly REM DO REM REM highlight this character REM LOCATE row, column COLOR white PRINT CHR$(character); REM REM get given character REM GOSUB getchar REM REM check if they match REM note that we don't want case sensivity REM IF UCASE$(CHR$(character)) = UCASE$(CHR$(givencharacter)) THEN REM REM they match, display it green REM and end REM COLOR green LOCATE row, column PRINT CHR$(character); done = 1 correctchars = correctchars + 1 REM REM adjust tick, but with a small relaxation factor REM IF (numpoints > 0) AND (numlines > 0) THEN usertick = ((pointtime / numpoints) + (linetime / numlines)) / 2 tick = ((10 * tick) + usertick) / 11 tick = INT(tick + .5) END IF ELSE REM REM don't match, display it red REM wait for error and do not end REM COLOR red LOCATE row, column PRINT CHR$(character); REM REM draw error (8 points) REM savedchar = character character = backspace GOSUB displaymorse character = savedchar REM REM remember wrong char for statistics REM wrongchars$ = wrongchars$ + CHR$(character) REM REM wait for error character REM DO GOSUB getchar LOOP WHILE givencharacter <> backspace REM REM redisplay desired character REM GOSUB displaymorse REM REM signal continuing and count errors REM done = 0 errors = errors + 1 END IF REM REM another try if character was wrong REM LOOP WHILE done = 0 END IF NEXT column NEXT row REM REM get end time REM endtime = TIMER REM REM close logfile REM CLOSE #8 REM REM print statistic REM GOSUB statistic REM REM save tick REM OPEN "tick.ini" FOR OUTPUT AS #5 PRINT #5, tick CLOSE #5 REM REM draw log REM first delete all variables because otherwise we run out of memory REM (hey, this is dos!) REM CLEAR GOSUB drawlog REM REM once more? REM PRINT "" INPUT "Einen weiteren Text geben (j/n) "; yesorno$ IF yesorno$ = "j" OR yesorno$ = "J" THEN GOTO start REM REM bye REM STOP REM REM subroutine initmorse - do everything to enable the decodeing REM initmorse: REM REM use bios segment REM DEF SEG = &H40 REM REM enable cts line and so enable the morse key REM OUT iobase + 4, 2 REM REM initialize coding REM DIM codetable$(maxcode) FOR counter = 1 TO maxcode codetable$(counter) = "#" NEXT counter codetable$(5) = "A" codetable$(67) = "B" codetable$(70) = "C" codetable$(22) = "D" codetable$(1) = "E" codetable$(43) = "F" codetable$(25) = "G" codetable$(40) = "H" codetable$(4) = "I" codetable$(53) = "J" codetable$(23) = "K" codetable$(49) = "L" codetable$(8) = "M" codetable$(7) = "N" codetable$(26) = "O" codetable$(52) = "P" codetable$(77) = "Q" codetable$(16) = "R" codetable$(13) = "S" codetable$(2) = "T" codetable$(14) = "U" codetable$(41) = "V" codetable$(17) = "W" codetable$(68) = "X" codetable$(71) = "Y" codetable$(76) = "Z" codetable$(161) = "1" codetable$(134) = "2" codetable$(125) = "3" codetable$(122) = "4" codetable$(121) = "5" codetable$(202) = "6" codetable$(229) = "7" codetable$(238) = "8" codetable$(241) = "9" codetable$(242) = "0" codetable$(203) = "==" codetable$(692) = "," codetable$(205) = ":" codetable$(400) = "?" codetable$(455) = "." codetable$(212) = "=" codetable$(151) = "+" codetable$(374) = "*" codetable$(3280) = CHR$(backspace) REM REM and now the table for the other way around, that is translating REM an ascii character into morse REM DIM morsetable$(maxmorse) FOR counter = 1 TO maxmorse morsetable$(counter) = "" NEXT counter morsetable$(ASC("A")) = ".-" morsetable$(ASC("B")) = "-..." morsetable$(ASC("C")) = "-.-." morsetable$(ASC("D")) = "-.." morsetable$(ASC("E")) = "." morsetable$(ASC("F")) = "..-." morsetable$(ASC("G")) = "--." morsetable$(ASC("H")) = "...." morsetable$(ASC("I")) = ".." morsetable$(ASC("J")) = ".---" morsetable$(ASC("K")) = "-.-" morsetable$(ASC("L")) = ".-.." morsetable$(ASC("M")) = "--" morsetable$(ASC("N")) = "-." morsetable$(ASC("O")) = "---" morsetable$(ASC("P")) = ".--." morsetable$(ASC("Q")) = "--.-" morsetable$(ASC("R")) = ".-." morsetable$(ASC("S")) = "..." morsetable$(ASC("T")) = "-" morsetable$(ASC("U")) = "..-" morsetable$(ASC("V")) = "...-" morsetable$(ASC("W")) = ".--" morsetable$(ASC("X")) = "-..-" morsetable$(ASC("Y")) = "-.--" morsetable$(ASC("Z")) = "--.." morsetable$(ASC(".")) = ".-.-.-" morsetable$(ASC(",")) = "--..--" morsetable$(ASC("?")) = "..--.." morsetable$(ASC("1")) = ".----" morsetable$(ASC("2")) = "..---" morsetable$(ASC("3")) = "...--" morsetable$(ASC("4")) = "....-" morsetable$(ASC("5")) = "....." morsetable$(ASC("6")) = "-...." morsetable$(ASC("7")) = "--..." morsetable$(ASC("8")) = "---.." morsetable$(ASC("9")) = "----." morsetable$(ASC("0")) = "-----" morsetable$(backspace) = "........" REM REM tick (shorter is point, longer is line) REM gettick: ON ERROR GOTO tickininotfound OPEN "tick.ini" FOR INPUT AS #4 ON ERROR GOTO genericerror INPUT #4, tick CLOSE #4 gottick: oldtick = tick REM REM initialize variables REM counter = 0 cwcode = 0 numberofchar = 0 pointtime = 0 numpoints = 0 linetime = 0 numlines = 0 RETURN REM REM subroutine getchar REM get one character given by the morse key REM getchar: REM REM check status of rts modem line REM note that we must wait until the operator starts to give the character REM REM REM delete the last line the user has given REM DRAW "C" + STR$(black) DRAW "BM0,12" DRAW "M639,12" DO REM REM if a key is pressed, the program has to morse the current REM character REM IF INKEY$ <> "" THEN GOSUB domorse keystatus = INP(iobase + 6) AND 16 IF keystatus <> 0 THEN keystatus = 1 LOOP WHILE keystatus = 0 REM REM remember that the old key status was 0 (we check this later) REM keystatus = 0 REM REM loop until character is finished REM finished = 0 REM REM counters REM counter = 0 drawcounter = 0 DO REM REM count REM counter = counter + 1 drawcounter = drawcounter + 1 REM REM check status of rts modem line REM oldkeystatus = keystatus keystatus = INP(iobase + 6) AND 16 IF keystatus <> 0 THEN keystatus = 1 REM REM draw given character REM we reset the last pixel if the key is not pressed and REM set the current pixel to mark where we are REM IF keystatus = 0 THEN DRAW "C" + STR$(black) DRAW "BM" + STR$(drawcounter - 1) + ",12" DRAW "M" + STR$(drawcounter - 1) + ",12" END IF DRAW "C" + STR$(yellow) DRAW "BM" + STR$(drawcounter) + ",12" DRAW "M" + STR$(drawcounter) + ",12" REM REM key gets pressed REM IF (keystatus = 1) AND (oldkeystatus = 0) THEN REM REM beeper on REM OUT &H43, 182 OUT &H42, 152 OUT &H42, 10 port = INP(&H61) port = port OR 3 OUT &H61, port REM REM save old counter, but not if we have just started to REM give this character (we see this by counter being 1) REM IF counter > 1 THEN PRINT #8, "+"; counter REM REM reinitialize counter REM counter = 0 END IF REM REM key gets unpressed REM IF (keystatus = 0) AND (oldkeystatus = 1) THEN REM REM beeper off REM port = INP(&H61) port = port AND 252 OUT &H61, port REM REM check short or long REM IF (counter <= tick) THEN REM REM point REM cwcode = 3 * cwcode + 1 pointtime = pointtime + counter numpoints = numpoints + 1 ELSE REM REM line REM cwcode = 3 * cwcode + 2 linetime = linetime + counter numlines = numlines + 1 END IF REM REM save old counter REM PRINT #8, "-"; counter REM REM reinitialize counter REM counter = 0 END IF REM REM long pause - character is finished REM IF (keystatus = 0) AND (counter = tick) THEN REM REM write length into the log file for later display REM note that the pause and one and a half time the tick REM lengthforlog = INT(tick * 1.5) PRINT #8, "+"; lengthforlog REM REM lookup given character REM IF cwcode > maxcode THEN givencharacter = ASC("#") ELSE givencharacter = ASC(codetable$(cwcode)) END IF REM REM reinitialize cwcode REM cwcode = 0 REM REM we return to the caller REM finished = 1 END IF REM REM very long line - change frequency of sound to inform REM the operator (one octave higher) REM IF warniflinetoolong = 1 THEN IF (keystatus = 1) AND (counter = INT(1.5 * tick + .5)) THEN OUT &H43, 182 OUT &H42, 76 OUT &H42, 5 END IF END IF LOOP WHILE finished = 0 RETURN REM REM subroutine statistic REM give statistic of characters and errors REM statistic: REM REM get seconds needed REM note: we took the times earlier - the routine timer returned the REM seconds sicne midnight REM secondsneeded = endtime - starttime REM REM may be we morse over midnight... REM in this case we get a negative time and have to add the number of REM seconds a day has REM IF secondsneeded < 0 THEN secondsneeded = secondsneeded + 24 * 60 * 60 REM REM actual statistics REM CLS COLOR white PRINT "Statistik fr Datei "; textfile$ PRINT "" PRINT "Zeit in Sekunden : "; PRINT USING "####.##"; secondsneeded PRINT "Zeichen pro Minute : "; PRINT USING "####.##"; correctchars / secondsneeded * 60 PRINT "Irrungen pro Minute : "; PRINT USING "####.##"; errors / secondsneeded * 60 PRINT "Strich/Punkt-Verh„ltnis : "; PRINT USING "####.##"; (linetime / numlines) / (pointtime / numpoints) PRINT "Tick am Anfang : "; PRINT USING "####.##"; oldtick PRINT "Tick am Ende : "; PRINT USING "####.##"; tick GOSUB sortwrongchars PRINT "Falsche Zeichen : "; wrongchars$ REM REM the user must type return to see the graphical display REM LOCATE 20, 1 INPUT "Weiter mit ", dummy$ RETURN REM REM subroutine filenotfound REM ask again for a file name, if not found filenotfound: IF ERR <> 0 THEN PRINT "" PRINT "Die angegebene Datei existiert nicht." PRINT "" GOTO getfilename END IF REM REM subroutine genericerror REM called in case of an unforseeable error REM genericerror: CLS COLOR white PRINT "Allgemeiner Fehler "; ERR PRINT "Programm wird abgebrochen!!!" SLEEP STOP REM REM subroutine comininotfound REM no com.ini exists, so we use com1: as a default REM comininotfound: oldcomdevice$ = "COM1:" RESUME cominput REM REM subroutine tickininotfound REM no tick.ini exists, so we use 150 as a default REM tickininotfound: tick = 150 RESUME gottick REM REM subroutine sortwrongchars REM sorts the string wrongchars (bubblesort) REM sortwrongchars: REM REM we upcase at first, so lowercase and uppercase letters are sorted in REM the same way REM wrongchars$ = UCASE$(wrongchars$) stringlength = LEN(wrongchars$) DO sorted = 1 FOR counter = 1 TO stringlength - 1 leftpart$ = LEFT$(wrongchars$, counter - 1) rightpart$ = RIGHT$(wrongchars$, stringlength - counter - 1) thischar$ = MID$(wrongchars$, counter, 1) nextchar$ = MID$(wrongchars$, counter + 1, 1) IF thischar$ > nextchar$ THEN wrongchars$ = leftpart$ + nextchar$ + thischar$ + rightpart$ sorted = 0 END IF NEXT counter LOOP WHILE sorted = 0 RETURN REM REM subroutine warnininotfound REM no warn.ini exists, so we use "no warning" as a default REM warnininotfound: oldwarn = 0 RESUME warninput REM REM subroutine domorse REM morse the current character REM domorse: REM REM first calculate point and line length from the current "tick" REM pointlength = tick / 2 linelength = tick * 3 / 2 pauselength = tick / 2 REM REM the ascii code of the current character is still stored in the REM variable character. we have to find the codetable offset for this REM character REM note that we have to upcase the character to find it in the codetable REM character$ = CHR$(character) character$ = UCASE$(character$) offset = 0 FOR codecounter = 1 TO maxcode IF codetable$(codecounter) = character$ THEN offset = codecounter NEXT codecounter REM REM if the character was not found we simply return REM IF offset = 0 THEN RETURN REM REM otherwise we decode the offset to morse signs (stored in the number REM system with base 3 with point as 1 and line as 2) REM morse$ = "" DO rest = offset MOD 3 IF rest = 1 THEN morse$ = "." + morse$ IF rest = 2 THEN morse$ = "-" + morse$ offset = offset \ 3 LOOP WHILE (offset <> 0) REM REM morse REM FOR morsecounter = 1 TO LEN(morse$) REM REM beeper on REM OUT &H43, 182 OUT &H42, 152 OUT &H42, 10 port = INP(&H61) port = port OR 3 OUT &H61, port REM REM point REM IF MID$(morse$, morsecounter, 1) = "." THEN FOR lengthcounter = 1 TO pointlength GOSUB dummyloop NEXT lengthcounter END IF REM REM line REM IF MID$(morse$, morsecounter, 1) = "-" THEN FOR lengthcounter = 1 TO linelength GOSUB dummyloop NEXT lengthcounter END IF REM REM beeper off REM port = INP(&H61) port = port AND 252 OUT &H61, port REM REM pause REM FOR lengthcounter = 1 TO pauselength GOSUB dummyloop NEXT lengthcounter NEXT morsecounter REM REM that's all REM RETURN REM REM subroutine dummyloop REM loop for a little while so that the signs the computer gives have REM normal length REM dummyloop: FOR dummycounter = 1 TO dummylooplength NEXT dummycounter RETURN REM REM subroutine drawlog REM draws what we have morsed REM drawlog: OPEN "cw.log" FOR INPUT AS #9 x = 0 y = 24 scale = 2 REM REM delete screen and announce REM CLS PRINT "Mitschrift:" REM REM loop over all entries REM DO WHILE NOT EOF(9) INPUT #9, line$ thisstep = VAL(MID$(line$, 3, LEN(line$) - 2)) x = x + thisstep / scale REM REM plus means start of line and end of blank REM IF LEFT$(line$, 1) = "+" THEN REM REM position cursor to the new position without drawing REM xint = INT(x + .5) DRAW "BM" + STR$(xint) + "," + STR$(y) REM PRINT x; y END IF REM REM minus means end of line and start of blank REM IF LEFT$(line$, 1) = "-" THEN REM REM start a new line if we went out of the screen REM IF x >= 640 THEN x = thisstep / scale y = y + 8 DRAW "BM" + STR$(0) + "," + STR$(y) END IF REM REM position cursor to the new position with drawing REM xint = INT(x + .5) DRAW "M" + STR$(xint) + "," + STR$(y) REM PRINT x; y END IF LOOP CLOSE #9 LOCATE 20, 1 INPUT "Weiter mit ", dummy$ RETURN REM REM subroutine displaymorse REM displays the morse signal as lines REM displaymorse: REM REM current length of points and lines REM linelength = INT((tick * 1.5) + .5) pointlength = INT((tick / 2) + .5) pauselength = pointlength REM REM delete a line drawed before, that is, draw a black line REM DRAW "C" + STR$(black) DRAW "BM0,8" DRAW "M639,8" REM REM set origin for drawing and color to yellow REM DRAW "C" + STR$(yellow) DRAW "BM0,8" x = 0 REM REM upcase character REM thischar$ = CHR$(character) thischar$ = UCASE$(thischar$) thischar = ASC(thischar$) REM REM get points and lines REM signal$ = morsetable$(thischar) IF signal$ <> "" THEN REM REM draw all the points and lines REM FOR signalcounter = 1 TO LEN(signal$) pointorline$ = MID$(signal$, signalcounter, 1) IF pointorline$ = "." THEN x = x + pointlength END IF IF pointorline$ = "-" THEN x = x + linelength END IF REM REM draw point or line REM DRAW "M" + STR$(x) + ",8" REM REM skip behind pause REM x = x + pauselength DRAW "BM" + STR$(x) + ",8" NEXT signalcounter END IF RETURN