TITLE " MORSE " ;PREMIERE VERSION OK = V10 LIST P=16F628,R=D include "p16f628.inc" ; __config H'3F29' ;Cette configuration permet le remplacement broche à broche du PIC 16F84 dans un montage ;( quartz pour l 'horloge externe et MCLR/ ) ; ;********************************************************************** ; f5hla ; Beacon CW pour f1pdx d'apres keyer morse ;Objectif : Création d’une balise expérimentale 23cm et 3cm mode CW ;Matériels mis en œuvre ;Cahier des charges du générateur d’indicatif ( Action F5HLA Gilles) ;Carte à base de pic ( type 16F84 ou plus récent) ;Génération d’un message CW via relais pour isolement galvanique ;Message à mémoriser 64 caractères minimum . ;Vitesse msg 10 mots par minutes ;Possibilité de modifier le msg via PC ou clavier ;Reset Watchdog toutes les 15 minutes ? ;Alim extérieure 12 volts ; ;Exemple MSG ;VVV= CQ CQ BALISE 23CM DE F1PDX LOC JN08XS DEP78 ANT DIP PWR 5 W ;Suivi d’une tonalité constante de 15 secondes puis retour au msg ; ;Balise 23 cm ;Equipement 1 : ;- Antenne à fente de 55 cm alimentation centrale ;- PWR 10 watts ;- Transverter DB6NT + TX 144 avec entrée CW (jack 3.5 mm) ; ;Equipement 2 secours : ;- 2 dipôles ou bigwheel ;- PWR 10 watts ;- TX 432 MHZ avec entrée CW (jack 3.5 mm) + Tripleur à Varactor + ampli ; ;Balise 3 cm ;Equipement 1 : ;- Antenne à fentes (2 x 10 ) alimentation Guide ;- PWR 5 watts ;- Transverter DB6NT + TX 144 avec entrée CW (jack 3.5 mm) ;MENU ;SIDETONE ; A=Sidetone enable ;IAMBIC ; I=Iambic mode toggle ;KEYMODE ; K=Keyer mode ;LOADUSER ; L=Load user RAM ;SPEED ; S=Speed ;TUNE ; T=Tune ;ASP_TOGGLE ; U=Autospace Toggle ;SWAP_TOGGLE ; X=Swap Paddles Toggle ;DUMPUSER ; D=Play user RAM ;OUTWPM ; W=Report Morse speed ; ;********************************************************************** ; Configuration ; #define BEACON_TIME 6 #define BEACON ; ;********************************************************************** ; MORSE CODE KEYER CONTROLLER ; PIC MODE = PIC16F84(A) CLK=4.0MHZ ; ;********************************************************************** ; ; Pinout and notes ; ; 16F84A, 4MHz P-DIP part ; ======================= ; pin name connection ; 1 RA2 N/C ; 2 RA3 N/C ; 3 RA4 N/C ; 4 MCLR/ thru 10k to +5v ; 5 VSS 0v ; 6 RB0 KEY ; 7 RB1 N/C ; 8 RB2 ST ; 9 RB3 Beacon ON/OFF (RB7) ; 10 RB4 DIT ; 11 RB5 DAH ; 12 RB6 N/C ; 13 RB7 PB ; 14 VDD +5v ! ; 15 O2 * ; 16 O1 * ; 17 RA0 N/C ; 18 RA1 N/C ; ; * O1, O2 connect to the outside pins of a 4MHz ceramic resonator. ; The center pin of the resonator goes to ground. ; ; ! A 0.01uF decoupling capacitor connects between pins 14 and 5. ; ; The unit is powered from 3, 1.5v cells in series. You may want to ; add an on/off switch (this is also the only way to reset the unit). ; ;------------------------------------------- ; Morse Equates Table ;------------------------------------------- M_END EQU 0x00 ; Ending delimiter M_SKIP EQU 0x01 ; Skip this entry M_SP EQU 0x02 ; Word space M_USER EQU 0x03 ; Toggle to USER RAM M_TO EQU 0x80 ; Timeout return from GETCW M_0 EQU 0xFC ; 0 1111 1100 = DAH-DAH-DAH-DAH-DAH M_1 EQU 0x7C ; 1 0111 1100 = DI-DAH-DAH-DAH-DAH M_2 EQU 0x3C ; 2 0011 1100 = DI-DI-DAH-DAH-DAH M_3 EQU 0x1C ; 3 0001 1100 = DI-DI-DI-DAH-DAH M_4 EQU 0x0C ; 4 0000 1100 = DI-DI-DI-DI-DAH M_5 EQU 0x04 ; 5 0000 0100 = DI-DI-DI-DI-DIT M_6 EQU 0x84 ; 6 1000 0100 = DAH-DI-DI-DI-DIT M_7 EQU 0xC4 ; 7 1100 0100 = DAH-DAH-DI-DI-DIT M_8 EQU 0xE4 ; 8 1110 0100 = DAH-DAH-DAH-DI-DIT M_9 EQU 0xF4 ; 9 1111 0100 = DAH-DAH-DAH-DAH-DIT M_AR EQU 0x54 ; 0101 0100 = DI-DAH-DI-DAH-DIT M_SK EQU 0x16 ; 0001 0110 = DI-DI-DI-DAH-DI-DAH M_PER EQU 0x56 ; 0101 0110 = DI-DAH-DI-DAH-DI-DAH M_COM EQU 0xCE ; 1100 1110 = DAH-DAH-DI-DI-DAH-DAH M_BT EQU 0x8C ; 1000 1100 = DAH-DI-DI-DI-DAH M_QUE EQU 0x32 ; ? 0011 0010 = DI-DI-DAH-DAH-DI-DIT M_DN EQU 0x94 ; 1001 0100 = DAH-DI-DI-DAH-DIT M_A EQU 0x60 ; A 0110 0000 = DI-DAH M_B EQU 0x88 ; B 1000 1000 = DAH-DI-DI-DIT M_C EQU 0xA8 ; C 1010 1000 = DAH-DI-DAH-DIT M_D EQU 0x90 ; D 1001 0000 = DAH-DI-DIT M_E EQU 0x40 ; E 0100 0000 = DIT M_F EQU 0x28 ; F 0010 1000 = DI-DI-DAH-DIT M_G EQU 0xD0 ; G 1101 0000 = DAH-DAH-DIT M_H EQU 0x08 ; H 0000 1000 = DI-DI-DI-DIT M_I EQU 0x20 ; I 0010 0000 = DI-DIT M_J EQU 0x78 ; J 0111 1000 = DI-DAH-DAH-DAH M_K EQU 0xB0 ; K 1011 0000 = DAH-DI-DAH M_L EQU 0x48 ; L 0100 1000 = DI-DAH-DI-DIT M_M EQU 0xE0 ; M 1110 0000 = DAH-DAH M_N EQU 0xA0 ; N 1010 0000 = DAH-DIT M_O EQU 0xF0 ; O 1111 0000 = DAH-DAH-DAH M_P EQU 0x68 ; P 0110 1000 = DI-DAH-DAH-DIT M_Q EQU 0xD8 ; Q 1101 1000 = DAH-DAH-DI-DAH M_R EQU 0x50 ; R 0101 0000 = DI-DAH-DIT M_S EQU 0x10 ; S 0001 0000 = DI-DI-DIT M_T EQU 0xC0 ; T 1100 0000 = DAH M_U EQU 0x30 ; U 0011 0000 = DI-DI-DAH M_V EQU 0x18 ; V 0001 1000 = DI-DI-DI-DAH M_W EQU 0x70 ; W 0111 0000 = DI-DAH-DAH M_X EQU 0x98 ; X 1001 1000 = DAH-DI-DI-DAH M_Y EQU 0xB8 ; Y 1101 1000 = DAH-DAH-DI-DAH M_Z EQU 0xC8 ; Z 1100 1000 = DAH-DAH-DI-DIT ; ;------------------------------------------- ; Code Speed Index Defines ; Each index points to a delay value in the ; code speed index table. Use the call IDX2SPD ; to convert index to speed delay. ; WPM_5 EQU 00D WPM_6 EQU 01D WPM_7 EQU 02D WPM_8 EQU 03D WPM_9 EQU 04D WPM_10 EQU 05D WPM_11 EQU 06D WPM_12 EQU 07D WPM_13 EQU 08D WPM_14 EQU 09D WPM_15 EQU 10D WPM_16 EQU 11D WPM_17 EQU 12D WPM_18 EQU 13D WPM_19 EQU 14D WPM_20 EQU 15D WPM_21 EQU 16D WPM_22 EQU 17D WPM_23 EQU 18D WPM_24 EQU 19D WPM_25 EQU 20D WPM_26 EQU 21D WPM_27 EQU 22D WPM_28 EQU 23D WPM_29 EQU 24D WPM_30 EQU 25D WPM_31 EQU 26D WPM_32 EQU 27D WPM_33 EQU 28D WPM_34 EQU 29D WPM_35 EQU 30D WPM_36 EQU 31D WPM_37 EQU 32D WPM_38 EQU 33D WPM_39 EQU 34D WPM_40 EQU 35D WPM_41 EQU 36D WPM_42 EQU 37D WPM_43 EQU 38D WPM_44 EQU 39D WPM_45 EQU 40D WPM_46 EQU 41D WPM_47 EQU 42D WPM_48 EQU 43D WPM_49 EQU 44D MAXSPDIDX EQU WPM_49 ;********** Customization Header ************** ; This is where to customize ; SPEED_DEFAULT EQU WPM_10 ; can be WPM_5 to WPM_49 CURMSG EQU MSG5-MSGBASE ; Select message 5: USER msg as def MAXCARRAM EQU 80 ; nb max de caracteres dans le message MODE_DEFAULT EQU 0B3H ; 0B3H = 1011 0011 tel que : ;STEN EQU 00H ; Sidetone enable // DOIT ETRE ON POUR UN BON DEMARRAGE ;STRQ EQU 01H ; Sidetone request ;SWAP EQU 03H ; Swap paddles enable bit ;IAM_A EQU 05H ; Iambic Mode A flag (A=1, B=0) ;SKM EQU 06H ; Straight Key Mode enable bit ;ASP EQU 07H ; Autospace enable bit PERIOD EQU 120D SPD_CONST EQU 212D ;16f84 ; ifdef BEACON ; ;development mode, code protect off, watchdog on ; __CONFIG _CP_OFF & _WDT_ON & _XT_OSC ; else ; ;manufacture mode, code protect off, watchdog off ; __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC ; endif ;16f628 ifdef BEACON ;development mode, code protect off, watchdog on __CONFIG _CP_OFF & _WDT_ON & _INTRC_OSC_NOCLKOUT & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF else ;manufacture mode, code protect off, watchdog off __CONFIG _CP_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _LVP_OFF endif ;**************** End of Customization Header ****************** ;------------------------------------------- ; PIC Specific Equates ;------------------------------------------- ; standard register defines are taken from the include file PC9 EQU Z ; makes BSF STATUS, PC9 (etc) no-ops PC EQU PCL ; for 8-bit PC adds, etc ;------------------------------------------- ; File Register Assignments ;------------------------------------------- GPIO EQU PORTB ; I/O Port - see below for changed pin assignments CBLOCK 0x20 ; Registers PROCLAT ; Process Latch DELAYHI ; High delay counter register DELAYLO ; Low delay counter register TIMEBASE ; Morse Time Base MODEREG ; Mode Bits WTEMP ; for loading PCLATH SPEEDIDX ; Current speed table index BX ; AL ; General Purpose Registers AH ; CL ; CH ; DH ; USERRAM: MAXCARRAM ; User Message ENDC ;------------------------------------------- ; MPU Bit Assignments ; ** 16F84A uses port B ** ;------------------------------------------- ; These bits also used in PROCLAT, differ from port assignments DITPDL EQU 00H ; IN DIT Paddle DAHPDL EQU 01H ; IN DAH Paddle PB EQU 03H ; IN Message/Config Push Button XDITPDL EQU 04H ; RB4, IN DIT Paddle XDAHPDL EQU 05H ; RB5, IN DAH Paddle KEY EQU 00H ; RB0, OUT Keyed Output XPB EQU 07H ; RB7, IN Message/Config Push Button TONE EQU 02H ; RB2, OUT Sidetone Output ifdef BEACON BCON EQU 03H ; RB3, OUT Beacon Request Output else STK EQU 03H ; RB3, OUT Keyed Sidetone Output endif ; ;------------------------------------------- ; Mode Bit Assignments ; ; Note: The STEN bit *must* be enabled in ; MODE_DEFAULT so the signon character will ; be audible, it gets setup properly at the ; beginning of SERVICE. ;------------------------------------------- STEN EQU 00H ; Sidetone enable STRQ EQU 01H ; Sidetone request SWAP EQU 03H ; Swap paddles enable bit IAM_A EQU 05H ; Iambic Mode A flag (A=1, B=0) SKM EQU 06H ; Straight Key Mode enable bit ASP EQU 07H ; Autospace enable bit STRQBIT EQU 02H ; Use this to XORF STRQ SWAPBIT EQU 08H ; Use this to XORF SWAP IABBIT EQU 20H ; Use this to XORF IAM_A SKMBIT EQU 40H ; Use this to XORF SKM ASPBIT EQU 80H ; Use this to XORF ASP ;------------------------------------------- ; PROCLAT Equates ;------------------------------------------- PDLMASK EQU 03H ; Mask: Paddle input bits PROCMASK EQU 0FCH ; Mask: Process bits INLAST EQU 02H ; Last input flag, 1 means Dit was just entered USERON EQU 03H ; Use USERRAM as message source when set CONVERSE EQU 04H ; Conversational mode flag (for DE message) OUTLAST EQU 05H ; Last output flasg, 1 means Dit was sent last BOTH_ON EQU 06H ; Both paddles were pressed, used in iambic mode B TXSQ EQU 07H ; Transmit Key Squelch bit in PROCLAT ;------------------------------------------- ; Miscellaneous Equates ;------------------------------------------- SELDELAY_H EQU 120D ; Delay constants for SELDELAY_L EQU 240D ; SELDELAY routine GC_TO_L EQU 220D ; GETCW timeout inside loop GC_TO_M EQU 170D ; GETCW timeout middle loop GC_TO_H EQU 3D ; GETCW timeout outside loop SPEEDCNT EQU 4D ; Number of characters sent in SPEED loop ;******************************************* ; MACROS ; 16F84A has address range and stack depth ; to implement calls directly w/o trickery ;******************************************* NCALL MACRO LABEL CALL LABEL ENDM NRETURN MACRO RETURN ENDM READEE macro ;la donnée se trouve dans W BANKSEL EEADR ; passer en banque1 movwf EEADR ; adresse à lire dans registre EEADR bsf EECON1 , RD ; lancer la lecture EEPROM movf EEDATA , w ; charger valeur lue dans W bcf STATUS , RP0 ; repasser en banque0 endm ; fin de la macro WRITEE macro ; la donnée se trouve dans W, l'adresse dans BX local wait BANKSEL EEADR ; passer en banque1 wait ifdef BEACON CLRWDT endif btfsc EECON1 , WR ; tester si écriture en cours goto wait ; oui, attendre movwf EEDATA ; placer data dans registre bcf STATUS , RP0 ; passer en banque0 movf BX,W ; charger adresse d'écriture BANKSEL EEADR ; repasser en banque1 movwf EEADR ; placer dans registre ; bsf STATUS , RP0 ; passer en banque1 bcf EECON1,EEIF ; effacer flag de fin d'écriture bsf EECON1,WREN ; autoriser accès écriture movlw 0x55 ; charger 0x55 movwf EECON2 ; envoyer commande movlw 0xAA ; charger 0xAA movwf EECON2 ; envoyer commande bsf EECON1 , WR ; lancer cycle d'écriture bcf EECON1,WREN ; verrouiller prochaine écriture bcf STATUS , RP0 ; repasser en banque0 endm ;******************************************* ; RESET ENTRY ;******************************************* ORG 0 GOTO INIT ORG 4 INTVEC GOTO INTVEC ; ;=========================================== ; Subroutines ; ; Must reside in first ; 256 bytes of codespace ;=========================================== ;=========================================== ; Sample and Latch Input State ;=========================================== SAMPLE NCALL NSAMPLE RETLW 0 NSAMPLE ifdef BEACON CLRWDT ; Reset watchdog timer endif BTFSC GPIO, XDITPDL GOTO RIGHT_TEST BTFSC MODEREG, SWAP GOTO NS_DAH1 LEFT_TEST BSF PROCLAT, DITPDL BSF PROCLAT, INLAST ; Set on DIT GOTO RIGHT_TEST NS_DAH1 BSF PROCLAT, DAHPDL BCF PROCLAT, INLAST ; Clear on DAH RIGHT_TEST BTFSC GPIO, XDAHPDL GOTO NS_DONE BTFSS MODEREG, SWAP GOTO NS_DAH2 BSF PROCLAT, DITPDL BSF PROCLAT, INLAST ; Clear on DAH GOTO NS_DONE NS_DAH2 BSF PROCLAT, DAHPDL BCF PROCLAT, INLAST ; Set on DAH NS_DONE NRETURN ;=========================================== ; Send DIT ;=========================================== DITOUT ifndef BEACON BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, STK ; Key Sidetone endif BTFSS PROCLAT, TXSQ ; Transmit Key Squelch ? BSF GPIO, KEY NCALL NDITDELAY ifndef BEACON BCF GPIO, STK ; Unkey Sidetone endif BCF GPIO, KEY NCALL NDITDLY_NT BCF PROCLAT, DITPDL ; Clear DIT bit in paddle register BSF PROCLAT, OUTLAST ; Mark that Dit was sent RETLW 0 ;=========================================== ; Send DAH ;=========================================== DAHOUT ifndef BEACON BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, STK ; Key Sidetone endif BTFSS PROCLAT, TXSQ ; Transmit Key Squelch ? BSF GPIO, KEY MOVLW 3 MOVWF BX NDLP NCALL NDITDELAY DECFSZ BX, F GOTO NDLP ifndef BEACON BCF GPIO, STK ; Unkey Sidetone endif BCF GPIO, KEY NCALL NDITDLY_NT BCF PROCLAT, DAHPDL ; Clear DAH bit in paddle register BCF PROCLAT, OUTLAST ; Mark that Dah was sent RETLW 0 ;=========================================== ; Send BEEP 15sec@10wpm ; 1wpm=50point pm ;=========================================== BEEPOUT ifndef BEACON BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, STK ; Key Sidetone endif BTFSS PROCLAT, TXSQ ; Transmit Key Squelch ? BSF GPIO, KEY MOVLW D'125' ;15s@10wpm MOVWF BX NDLPBEEP NCALL NDITDELAY DECFSZ BX, F GOTO NDLPBEEP ifndef BEACON BCF GPIO, STK ; Unkey Sidetone endif BCF GPIO, KEY NCALL NDITDLY_NT RETLW 0 ; return ;=========================================== ; Wait sidetone delay with transmitter ; keyed. Used by TUNE & KEYDIRECT ; TUNE and KEYDIRECT call NDITDELAY thru ; STCLK, which sets an outside loop count ; of 1 and a reduced sidetone "low" time. ; This gives us a better match between ; KEYER and TUNE/KEYDIRECT sidetone. ;=========================================== STCLK MOVLW D'10' ; Ten passes GOTO DITDEL ;=========================================== ; Dit Delay ; ; Set delay period to be 1.15mSec to give a ; sidetone frequency of 800Hz. ;=========================================== NDITDELAY MOVF TIMEBASE, W ; Get interval DITDEL MOVWF DELAYHI MOVLW PERIOD-D'4' ; Compensate for NSAMPLE GOTO DITDEL15 DITDEL0 MOVLW PERIOD ; 1/2 Period High, 575 us DITDEL15 MOVWF DELAYLO DITDEL1 GOTO DTL0 DTL0 DECFSZ DELAYLO, F ; Inner Loop 1 Test GOTO DITDEL1 BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, TONE ; Key Sidetone MOVLW PERIOD ; 1/2 Period Low, 575 us MOVWF DELAYLO DITDEL2 GOTO DTL1 DTL1 DECFSZ DELAYLO,F ; Inner Loop 2 Test GOTO DITDEL2 BCF GPIO, TONE ; Unkey Tone DECFSZ DELAYHI,F ; Outer Loop Test GOTO DITDEL0 GOTO NSAMPLE ; Latch early input ; NRETURN from there ; ;=========================================== ; Dit Delay without Sidetone ;=========================================== NDITDLY_NT MOVF TIMEBASE, W ; Get interval MOVWF DELAYHI DD_NT0 MOVLW PERIOD ; 1/2 Period MOVWF DELAYLO DD_NT1 GOTO DNT0 DNT0 GOTO DNT1 DNT1 GOTO DNT2 DNT2 NOP DECFSZ DELAYLO, F ; Inner Loop Test GOTO DD_NT1 DECFSZ DELAYHI,F ; Outer Loop Test GOTO DD_NT0 GOTO NSAMPLE ; Latch early input ; NRETURN from there ; ;=========================================== ; Autospace Handler ; ; The idea is to keep the morse "pipe" full, ; that means that there should always be ; something in PROCLAT to send after the ; current dit or dah is sent. If not it is ; interpreted as an intercharacter space ; and a pause is inserted to fill out the ; remainder of a letterspace period. ; Any paddle events will be recorded in ; PROCLAT and issued in the order received ; thanks to the INLAST bit. ;=========================================== AUTOSP CALL SAMPLE ; Refresh PROCLAT BTFSS MODEREG, ASP ; Leave if autospace disabled GOTO SERVLOOP BTFSS PROCLAT, DITPDL ; Check if any in latch BTFSC PROCLAT, DAHPDL GOTO SERVLOOP ; Yes: go send CALL LETTERSPACE ; No: wait full letterspace GOTO SERVLOOP ; before allowing more sending ; ;=========================================== ; Word and Letter Spacing ; ; Remember that all characters have one ; dit delay tacked on automatically so ; only two are needed to provide a three ; bit letterspace. Six are needed for a ; seven bit word space. ;=========================================== WORDSPACE MOVLW 6 GOTO WLSPAC0 LETTERSPACE MOVLW 2 WLSPAC0 MOVWF BX WLSPAC1 NCALL NDITDLY_NT DECFSZ BX, F GOTO WLSPAC1 RETLW 0 ;=========================================== ; Output Single Morse Character ; Uses AL ;=========================================== OSCHAR MOVWF AL ; Copy character OSLOOP MOVF AL, W ; Get coded morse ADDWF AL, F ; AL*2, if (Z==0) DONE else C=DIT/DAH BTFSS STATUS, Z ; Skip if zero GOTO OSCONT CALL LETTERSPACE ; Inter-letter space RETLW 0 ; All done, return OSCONT BTFSC STATUS, C ; (C==1) then DAH else DIT GOTO OSDAH CALL DITOUT GOTO OSLOOP OSDAH CALL DAHOUT GOTO OSLOOP ;=========================================== ; Decimal to Morse Conversion Table ;=========================================== DEC2CW MOVWF WTEMP MOVLW HIGH($) MOVWF PCLATH MOVF WTEMP,W ADDWF PC, F ; Jump through table DECTBL RETLW M_0 RETLW M_1 RETLW M_2 RETLW M_3 RETLW M_4 RETLW M_5 RETLW M_6 RETLW M_7 RETLW M_8 RETLW M_9 ;=========================================== ; Cross Page Jumps ; ; Note 1: Routines in high page assume they ; called from low page and clear PC9 before ; they return. It is not possible to call ; a high page routine from the high page. ; ; Note 2: Subroutines above the 256 byte ; boundary are called through this interface. ; A call is converted to a jump, the called ; subroutine in high page ends with a return ; which pops the full 10 bit return address ; of the low page caller. ;=========================================== L_SENDMSG BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO SENDMSG L_CW2IDX BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO CW2IDX L_IDX2SPD BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO IDX2SPD L_GETCW BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO GETCW L_CONV_HI BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO CONV_HI L_CONV_LO BSF STATUS, PC9 ; Set PAGE bit for long GOTO GOTO CONV_LO ;=========================================== ; Long delay for user prompts ; Uses: CL ; Returns: ; CL MSB=1 if paddle pressed, 0 if not ;=========================================== SELDELAY MOVLW SELDELAY_H ; Set high select delay MOVWF DELAYHI SELD0 MOVLW SELDELAY_L ; Set low select delay MOVWF DELAYLO SELD1 CALL SAMPLE ; Latch paddle press GOTO SLD0 SLD0 DECFSZ DELAYLO, F ; Inner Loop 1 Test GOTO SELD1 DECFSZ DELAYHI, F ; Inner Loop 1 Test GOTO SELD0 BTFSS PROCLAT, DAHPDL ; DAH closed ? BTFSC PROCLAT, DITPDL ; DIT closed ? GOTO SDYES SDNO BCF CL, 0 ; Timed out w/no paddle pressed GOTO SDRET SDYES BSF CL, 0 ; A paddle was pressed SDRET RETLW 0 ;=========================================== ; Push Button Switch Handler ; Converse mode is defined as the phase ; where the user is actually entering some ; morse letters. We want letterspace off ; to improve response, and we want DE mode ; off to prevent undesired output. ;=========================================== CONFIG0 ; ------- Disable Letterspace Here ----------- BSF PROCLAT, CONVERSE ; Start converse mode BSF PROCLAT, TXSQ ; Disable Key BSF MODEREG, STEN ; Force sidetone MOVLW M_R ; Signal user to enter code CALL OSCHAR CONFWT CLRWDT BTFSS GPIO, XPB ; Still closed ? GOTO CONFWT ; wait till released CALL L_GETCW ; Get user response CALL L_CW2IDX ; Translate CW to table index BCF STATUS, PC9 ; Clear page bit MOVWF WTEMP MOVLW HIGH($) MOVWF PCLATH MOVF WTEMP,W ADDWF PC, F ; Jump through table GOTO SIDETONE ; A=Sidetone enable GOTO IAMBIC ; I=Iambic mode toggle GOTO KEYMODE ; K=Keyer mode GOTO LOADUSER ; L=Load user RAM GOTO SPEED ; S=Speed GOTO TUNE ; T=Tune GOTO ASP_TOGGLE ; U=Autospace Toggle GOTO SWAP_TOGGLE ; X=Swap Paddles Toggle ; ------- Allow Letterspace for below --------- GOTO DUMPUSER ; D=Play user RAM GOTO OUTWPM ; W=Report Morse speed GOTO QUERET ; Unknown entry !?!? ; Use common return ;******************************************* ; Wake Up Handler ; BX is used as a beacon timer, it is ; initialized at the end of QVRET. ; The OPTION register is setup so that WDT ; will cause a reset every 2 seconds. From ; INIT we will get vectored here. Where ; BX will be checked to see if it is zero. ; If so pin 2 will be configured as an output ; and asserted low true. If BX is not zero ; it is is decremented. Note that a pin ; change will be handled in the same manner ; as a WDT timeout but this is moot since ; the beacon enable switch will be off in ; non-beacon mode. BX will normally rest at ; a value of zero courtesy of the DAHOUT and ; LETTERSPACE routines. ;******************************************* ifdef BEACON SERVICE MOVF BX, W ; Test beacon timer BTFSC STATUS, Z ; If zero: Beacon ON GOTO BEAC_ON DECF BX, F ; Else: Decrement beacon timer GOTO WAKEUP ; Continue in case it was ; a pin change wakeup BEAC_ON BCF GPIO, BCON ; Insure that GP5 reg is zero MOVLW B'11110000' ; Make RB3 an output BSF STATUS, RP0 MOVWF TRISB ^ 0x80 ; to assert beacon request BCF STATUS, RP0 ;******************************************* ;* MAIN GPIO Service Routine * ;******************************************* WAKEUP CLRWDT else SERVICE endif BCF MODEREG, STEN ; Restore user's BTFSC MODEREG, STRQ ; sidetone BSF MODEREG, STEN ; preference. CLRF PROCLAT ; Clear TXSQ and any latched inputs SERVLOOP CALL SAMPLE BTFSS MODEREG, SKM ; Straight key mode ? GOTO KEYER ; No: Go keyer BTFSC PROCLAT, DAHPDL ; DAH paddle pressed ? GOTO KEYDIRECT ; Yes: Go straight key GOTO PBTEST ; No: Test pushbutton ; ; Iambic operation: If iambic mode is enabled the following state ; machine outputs alternating dits and dahs when both paddles are ; pressed at the same time. DIT_PADDLE = GPIO, DIT_PADDLE_L = PROC_LAT ; ; if ((DIT PADDLE && DAH PADDLE) ; { ; set BOTH_ON "ref. Mode B" ; if (DIT LAST) { ; send DAH ; clear DIT LAST ; } ; else { ; (this also handles odd case where both paddles ; hit simultaneously) ; send DIT ; set DIT LAST ; } ; } ; else if (BOTH_ON == TRUE) { "ref: Mode B, BOTH_ON is ; clear BOTH_ON never set in MODE A" ; if (DIT LAST) ; send DAH ; else ; send DIT ; } ; else { ; if (L_DIT_PADDLE && L_DAH_PADDLE) { ; if (INLAST) { ; send DAH ; clear DIT LAST ; } ; else { ; send DIT ; set DIT LAST ; } ; } ; if (DIT PADDLE) { ; send DIT ; set DIT LAST ; } ; else if (DAH PADDLE) { ; send DAH ; clear DIT LAST ; } ; else { ; (if neither paddle is set, do nothing) ; } ; } ; } KEYER ; ; Iambic mode A and B operation depend on PROCLAT, BOTH_ON and current ; state of the paddles. In mode B PROCLAT is used to check if the ; both paddles were pressed at the end of toggle mode to see if an ; extra dit or dah are sent. ; In mode A the PROCLAT is cleared when leaving toggle mode to ; prevent any additional dits or dahs from being sent. ; ; We check GPIO here since we need to monitor real time paddle ; status, the case where both are set in PROCLAT must be ; handled specially since order must be observed. BTFSS GPIO, XDITPDL ; DIT paddle *and* BTFSC GPIO, XDAHPDL ; DAH paddle pressed ? GOTO CHK_BOTH_ON ; No: Go check if both is pending ; Both Paddles are pressed BSF PROCLAT, BOTH_ON ; Set both paddles on flag TOGGLE BTFSC PROCLAT, OUTLAST ; If LAT==1 (dit was last) then DAH GOTO LPDAH ; GOTO LPDIT ; else, always DIT. This addresses ; the case where both paddles hit at ; the same time. ; Both paddles aren't pressed, check if they just were ; and if in MODE B, issue alternate. Otherwise just ; check for single dit or dah request. CHK_BOTH_ON BTFSS PROCLAT, BOTH_ON GOTO CHK_SINGLE BCF PROCLAT, BOTH_ON BTFSS MODEREG, IAM_A GOTO TOGGLE ; Mode B, send one more CLRF PROCLAT ; Mode A, kill any in latch GOTO AUTOSP ; First check to see if both bits are set, if they are we ; need to figure out which one to send first. INLAST tells ; the last paddle input received, so the opposite is sent. CHK_SINGLE BTFSC PROCLAT, DITPDL BTFSS PROCLAT, DAHPDL GOTO CHK_S1 BTFSC PROCLAT, INLAST ; INLAST = 1 if DIT was last GOTO LPDAH ; Fall thru, and a DIT gets sent CHK_S1 BTFSC PROCLAT, DITPDL ; DIT Paddle closed when = 1 GOTO LPDIT BTFSS PROCLAT, DAHPDL ; DAH Paddle closed when = 1 GOTO PBTEST LPDAH CALL DAHOUT GOTO AUTOSP LPDIT CALL DITOUT GOTO AUTOSP PBTEST BTFSS GPIO, XPB ; Push Button closed when = 0 GOTO PBHANDLE ; changed logic of skip (imw) ; ;=========================================== ; Go into sleep mode until one ; of the switches are hit or ; Watchdog timer times out. ;=========================================== ; SLEEP ; RB change will wake us up. Since GIE not enabled, arrive NOP ; here. BCF INTCON, 0 ; clear RBIF GOTO SERVICE ; resume PBHANDLE ; jump logic changed for jump around sleep, etc ifdef BEACON MOVLW B'11111000' ; Restore Pin 2 as an input BSF STATUS, RP0 ; to cancel beacon request MOVWF TRISB ^ 0x80 BCF STATUS, RP0 endif CALL SELDELAY BTFSC GPIO, XPB ; Is Push Button closed ? (==0) GOTO PBMSG ; No: output message CALL SELDELAY ; Yes: wait again BTFSS GPIO, XPB ; Is Push Button closed ? (==0) GOTO CONFIG0 ; Yes: do config routine PBMSG ; No: output message MOVLW CURMSG ; Point to 'current' message CALL L_SENDMSG ; We will return to caller from there CALL BEEPOUT ;15sec@10wpm MOVLW BEACON_TIME MOVWF BX GOTO SERVICE ;=========================================== ; Key transmitter for tuning ;=========================================== TUNE BTFSS MODEREG, STRQ ; Restore sidetone BCF MODEREG, STEN ; preference. ifndef BEACON BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, STK ; Key Sidetone endif BSF GPIO, KEY ; Key transmitter TUNELP CALL STCLK ; Key XMTR and delay w/sidetone BTFSC PROCLAT, DAHPDL ; DAH paddle ends tune GOTO WAIT4OFF BTFSS PROCLAT, DITPDL ; DIT paddle ends tune GOTO TUNELP ; Fall through ;=========================================== ; Wait for paddle release ;=========================================== WAIT4OFF MOVLW 25D ; debounce count MOVWF AL WAIT4LP ifdef BEACON CLRWDT ; Reset watchdog timer endif BTFSC GPIO, XDITPDL ; Make sure paddles BTFSS GPIO, XDAHPDL ; remain unpressed GOTO WAIT4OFF DECFSZ AL, F GOTO WAIT4LP ifndef BEACON BCF GPIO, STK ; Unkey Sidetone endif BCF GPIO, KEY ; Unkey transmitter GOTO SERVICE ;=========================================== ; Key XMTR for straight key mode ; ; We only look at one paddle for straight ; keying but the user can select which one ; with the swap command. ;=========================================== KEYDIRECT BTFSS MODEREG, STRQ ; Restore sidetone BCF MODEREG, STEN ; preference. ifndef BEACON BTFSC MODEREG, STEN ; Sidetone enabled ? BSF GPIO, STK ; Key Sidetone endif BSF GPIO, KEY ; Key transmitter KEYDLP BCF PROCLAT, DAHPDL ; Clear latched DAH CALL STCLK ; Key XMTR and delay w/sidetone BTFSC PROCLAT, DAHPDL ; DAH paddle still pressed ? GOTO KEYDLP ; Yes: Loop GOTO WAIT4OFF ; No: Leave via debounce ;=========================================== ; Set New CW Sending Speed ; Uses: AH, CL, CH ;=========================================== SPEED CLRF CH ; Init pass count SPLOOP MOVLW M_E ; Ask for Number CALL OSCHAR CALL L_GETCW ; Get user response in AH MOVLW M_TO ; Did user just sit there ? SUBWF AH, W BTFSC STATUS, Z GOTO QUERET ; Yes, end it BTFSC CH, 0 ; Skip on 1st pass GOTO SPSUM CALL L_CONV_HI ; Convert AH to 10's BCF STATUS, PC9 ; Clear page bit MOVWF CL ; Save MSN BSF CH, 0 ; Set pass flag GOTO SPLOOP SPSUM CALL L_CONV_LO ; Convert AH to 1's BCF STATUS, PC9 ; Clear page bit ADDWF CL, F ; Add in MSN BTFSS STATUS, C ; If both are valid both MSBs = 1 GOTO QUERET ; So if Carry=1 we have good values MOVLW -5D ; Adjust for indexing ADDWF CL, W ; And verify low limit BTFSS STATUS, C ; C==1 if >= 5, error if not GOTO QUERET MOVWF SPEEDIDX CALL L_IDX2SPD ; Convert index to delay constant BCF STATUS, PC9 ; Clear page bit MOVWF TIMEBASE ; Store it away GOTO COMRET_R QUERET MOVLW M_QUE ; Send ? for error GOTO COMRET ;=========================================== ; Message Selection ;=========================================== MSGDONE MOVLW M_R ; Yes: Send an 'R' for acknowledgement CALL OSCHAR GOTO WAIT4OFF ; Common "wait then return" ;=========================================== ; Paddle Swap Toggle ;=========================================== SWAP_TOGGLE MOVLW SWAPBIT GOTO COMXOR ; Shared XOR ;=========================================== ; Sidetone Toggle ;=========================================== SIDETONE MOVLW STRQBIT GOTO COMXOR ; Shared XOR ;=========================================== ; Toggle Keyer Mode ; Default is keyer mode, everytime this ; is called the mode will toggle between ; keyer and straight key mode. ;=========================================== KEYMODE MOVLW SKMBIT COMXOR XORWF MODEREG, F ; Shared XOR GOTO MSGDONE ; Common return, R for ack ; ;=========================================== ; Toggle Iambic Mode ; Toggle the iambic mode between Mode A ; which is not self completing and Mode B ; which is. Self completing means an extra ; alternate element is sent after both ; paddles are released. ;=========================================== IAMBIC MOVLW IABBIT XORWF MODEREG, F MOVLW M_A ; Assume Iambic mode A BTFSS MODEREG, IAM_A ; A=1, B=0 MOVLW M_B ; Assumed wrong, it's B GOTO COMRET ;=========================================== ; Autospace Toggle ;=========================================== ASP_TOGGLE MOVLW ASPBIT XORWF MODEREG, F MOVLW M_A ; Assume Autospace mode BTFSS MODEREG, ASP ; Autospace=1, Normal=0 MOVLW M_N GOTO COMRET ;=========================================== ; Load RAM Routine ; ; for loading user message. ; ; Uses AH, CL ;=========================================== LOADUSER MOVLW USERRAM ; Point to start of user string MOVWF FSR MOVLW MAXCARRAM-1 ; Max string length LU00 MOVWF CL ; Length in reg CL LULOOP MOVLW M_E ; Signal user to enter code CALL OSCHAR CALL L_GETCW ; Get a letter in AH MOVLW M_PER ; Did user explicitly end ? SUBWF AH, W BTFSC STATUS, Z GOTO LUDONE ; Yep, leave MOVLW M_TO ; Did user just sit there ? SUBWF AH, W BTFSS STATUS, Z GOTO LU0 ; No, we have a valid character MOVLW M_SP ; Yes, they want a space GOTO LU1 LU0 MOVF AH, W ; Get char LU1 MOVWF INDF ; Store in RAM INCF FSR, F ; Bump counter MOVLW M_END ; Append an EOS token MOVWF INDF ; Store in RAM DECFSZ CL, F ; Decrement length count GOTO LULOOP LUDONE movlw MAXCARRAM movwf BX ; GOTO COMRET_R LUEE DECF BX,F ;adresse en ram et en indirect movf BX, W ADDLW USERRAM MOVWF FSR ; Use indirect addressing MOVF INDF,W ;value WRITEE ; adresse=BX, value=W MOVF BX,F BTFSS STATUS,Z goto LUEE ; Fall thru to common return ;=========================================== ; Common Return ;=========================================== COMRET_R MOVLW M_R ; Send a "roger" COMRET CALL OSCHAR GOTO SERVICE ; ;=========================================== ; Dump User String ;=========================================== DUMPUSER MOVLW MSG5-MSGBASE ; Point to USER message QVRET CALL L_SENDMSG ; We will return to caller from there MOVLW BEACON_TIME MOVWF BX GOTO SERVICE ;=========================================== ; Report Morse speed ;=========================================== OUTWPM MOVLW 5D ; Slowest speed ADDWF SPEEDIDX, W ; Add index OUTDEC CLRF CH ; Clear tens register MOVWF CL ; Copy decimal number MOVLW 10D ; Base 10 ODLOOP SUBWF CL, F ; Extract 10's BTFSS STATUS, C ; C==0 : Underflow GOTO ODNEG INCF CH, F ; Incr 10's GOTO ODLOOP ODNEG ADDWF CL, F ; Restore ones to positive MOVF CH, W ; Output 10's first BTFSC STATUS, Z ; Suppress leading zero GOTO ODONES CALL DEC2CW BCF STATUS, PC9 ; Clear page bit CALL OSCHAR ODONES MOVF CL, W ; Output 1's CALL DEC2CW BCF STATUS, PC9 ; Clear page bit CALL OSCHAR MOVLW MSG8-MSGBASE ; Send WPM string GOTO QVRET ; Use shared return ;******************************************* ;* Initialization Routine * ;******************************************* INIT ifdef BEACON MOVLW B'11111000' ; IN:RB7,6,5,4,3 OUT:RB2,1,0 else MOVLW B'11110000' ; IN:RB7,6,5,4 OUT:RB3,2,1,0 endif BSF STATUS, RP0 MOVWF TRISB ^ 0x80 MOVLW 0 MOVWF TRISA ^ 0x80 ; A = all outputs MOVLW B'00001110' ; Enable PUs, Pscl->WDT MOVWF OPTION_REG ^ 0x80 ; Timer Clk=Internal, Prescale=1:64 BCF STATUS, RP0 CLRF GPIO ; Note: Beacon Request is asserted ; but not active since RB3 is an input MOVLW B'00001000' ; Enable RB port change interrupt (but NOT GIE) MOVWF INTCON ifdef BEACON BTFSC STATUS, NOT_TO ; Is this is dead man WDT ? GOTO INITCONT ; NO MOVLW M_W ; Send Watchdog wakeup GOTO COMRET ; Use common return entry ; GOTO SERVICE ; else: Go do wakeup stuff else ; TODO - implement wake-on-interrupt endif INITCONT MOVLW SPEED_DEFAULT ; Initialize Morse Timebase MOVWF SPEEDIDX CALL L_IDX2SPD ; Convert index to delay constant BCF STATUS, PC9 ; Clear page bit MOVWF TIMEBASE ; Store it away MOVLW MODE_DEFAULT ; Load MODEREG default MOVWF MODEREG movlw MAXCARRAM movwf BX INITMSG CLRWDT DECF BX,F movf BX, W READEE movwf AL ; et initialiser compteur de passages MOVF BX,W ADDLW USERRAM MOVWF FSR ; Use indirect addressing MOVF AL,W MOVWF INDF MOVF BX,F BTFSS STATUS,Z goto INITMSG bsf STATUS , RP0 ; passer en banque1 WAITMSG CLRWDT btfsc EECON1 , RD GOTO WAITMSG bcf STATUS , RP0 ; repasser en banque0 BSF PROCLAT, TXSQ ; Don't transmit, just run sidetone ; SERVICE will reset TXSQ MOVLW M_R ; Send R for hello BTFSS GPIO, XDAHPDL ; Hook to get version MOVLW M_B ; Send Version ID GOTO COMRET ; Use common return entry ;=========================================== ; Page Boundary ;=========================================== ORG 0200h ;=========================================== ;=========================================== ; Move W to (FSR++) ;=========================================== XFRIDX MOVWF INDF INCF FSR, F RETLW 0 ;=========================================== ; Message Table ;=========================================== ; Call with message pointer in DH reg ; Table entry *DH++ is returned in W. ; We start at a new page to allow us to have ; a long message table which we can be sure ; will never cross a page boundary. ; Note: CALLs and computed jumps will always ; clear the PC[8] bit. PC[9] is sourced from ; STATUS reg bit 5. GETMSG MOVF DH, W INCF DH, F MOVWF WTEMP MOVLW HIGH($) MOVWF PCLATH MOVF WTEMP,W ADDWF PC, F ; Jump through table ;----------------------------------------------------- MSGBASE MSG5 RETLW M_V RETLW M_V RETLW M_V RETLW M_BT RETLW M_SP RETLW M_USER ; User Message RETLW M_SP RETLW M_END ; MSG8 RETLW M_SP ; WPM suffix RETLW M_W RETLW M_P RETLW M_M RETLW M_END ;=========================================== ; Code Speed Table ;=========================================== ;------------------------------------------- ; TIMEBASE Defaults ; ; Using ARRL Handbook as a reference, a 5 WPM dit ; is 240 msec, a dah is 720 ms. ; Formula: ; ; Count= (((5/WPM) * 240) / SIDETONE_PERIOD) ; ; For Sidetone Freq of 870 Hz (1.15 ms): ; ; Count = ((5/WPM) * (240/1.15)) ; SVAL EQU (5D * SPD_CONST) ; Fudged By oscilloscope verification ; ;------------------------------------------- IDX2SPD MOVWF WTEMP MOVLW HIGH($) MOVWF PCLATH MOVF WTEMP,W ADDWF PC, F ; Jump through table SPEEDTBL RETLW SVAL/5D ; WPM_5 RETLW SVAL/6D ; WPM_6 RETLW SVAL/7D ; WPM_7 RETLW SVAL/8D ; WPM_8 RETLW SVAL/9D ; WPM_9 RETLW SVAL/10D ; WPM_10 RETLW SVAL/11D ; WPM_11 RETLW SVAL/12D ; WPM_12 RETLW SVAL/13D ; WPM_13 RETLW SVAL/14D ; WPM_14 RETLW SVAL/15D ; WPM_15 RETLW SVAL/16D ; WPM_16 RETLW SVAL/17D ; WPM_17 RETLW SVAL/18D ; WPM_18 RETLW SVAL/19D ; WPM_19 RETLW SVAL/20D ; WPM_20 RETLW SVAL/21D ; WPM_21 RETLW SVAL/22D ; WPM_22 RETLW SVAL/23D ; WPM_23 RETLW SVAL/24D ; WPM_24 RETLW SVAL/25D ; WPM_25 RETLW SVAL/26D ; WPM_26 RETLW SVAL/27D ; WPM_27 RETLW SVAL/28D ; WPM_28 RETLW SVAL/29D ; WPM_29 RETLW SVAL/30D ; WPM_30 RETLW SVAL/31D ; WPM_31 RETLW SVAL/32D ; WPM_32 RETLW SVAL/33D ; WPM_33 RETLW SVAL/34D ; WPM_34 RETLW SVAL/35D ; WPM_35 RETLW SVAL/36D ; WPM_36 RETLW SVAL/37D ; WPM_37 RETLW SVAL/38D ; WPM_38 RETLW SVAL/39D ; WPM_39 RETLW SVAL/40D ; WPM_40 RETLW SVAL/41D ; WPM_41 RETLW SVAL/42D ; WPM_42 RETLW SVAL/43D ; WPM_43 RETLW SVAL/44D ; WPM_44 RETLW SVAL/45D ; WPM_45 RETLW SVAL/46D ; WPM_46 RETLW SVAL/47D ; WPM_47 RETLW SVAL/48D ; WPM_48 RETLW SVAL/49D ; WPM_49 ;=========================================== ; SENDMSG: ; Sends message pointed to by W ; Be careful of case where message table ; grows so large that it pushes this code ; above 0xff or a CALL instruction will ; not be able to reach it. ; Uses AL, DH (DH = MSGIDX) ;=========================================== SENDMSG MOVWF DH ; Store index S0LOOP BTFSC PROCLAT, USERON ; Determine data source GOTO S0USER CALL GETMSG ; Get *DH++ in W GOTO S0CONT S0USER MOVF INDF, W ; Get *USERRAM++ INCF FSR, F S0CONT MOVWF AL MOVLW M_END ; End delimiter ? SUBWF AL, W BTFSS STATUS, Z ; Skip if no compare GOTO S1CONT BTFSS PROCLAT, USERON ; End of user or end of message ? GOTO S1DONE BCF PROCLAT, USERON GOTO S0LOOP S1DONE BCF STATUS, PC9 ; Clear PAGE bit, we are done here BCF PROCLAT, USERON ; Clear User mode (case: user msg aborted) RETLW 0 ; All done, return S1CONT MOVLW M_USER ; Toggle to User Mode ? SUBWF AL, W BTFSC STATUS, Z ; Skip if no compare GOTO S1USER MOVLW M_SKIP ; Skip entry ? SUBWF AL, W BTFSC STATUS, Z ; Skip if no compare GOTO S0LOOP MOVLW M_SP ; Word space ? SUBWF AL, W BTFSS STATUS, Z ; Skip if compare GOTO S2LOOP BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL WORDSPACE ; Word Space = 7 time bits BSF STATUS, PC9 ; Set PAGE bit on return GOTO S0LOOP S1USER MOVLW USERRAM ; Point to start of user string S100 MOVWF FSR BSF PROCLAT, USERON GOTO S0LOOP S2LOOP BTFSS GPIO, XDAHPDL GOTO ABORT1 BTFSC GPIO, XDITPDL GOTO NOABORT ABORT1 ifdef BEACON CLRWDT ; Reset watchdog timer endif CLRF PROCLAT BTFSC GPIO, XDAHPDL ; Wait till hands off paddle BTFSS GPIO, XDITPDL GOTO ABORT1 GOTO S1DONE NOABORT MOVF AL, W ; Get coded morse ADDWF AL, F ; AL*2, if (Z==0) DONE else C=DIT/DAH BTFSS STATUS, Z ; Skip if zero GOTO S2CONT BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL LETTERSPACE ; Inter-letter space BSF STATUS, PC9 ; Set PAGE bit on return GOTO S0LOOP S2CONT BTFSC STATUS, C ; (C==1) then DAH else DIT GOTO S2DAH BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL DITOUT BSF STATUS, PC9 ; Set PAGE bit on return GOTO S2LOOP S2DAH BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL DAHOUT BSF STATUS, PC9 ; Set PAGE bit on return GOTO S2LOOP ;=========================================== ; Get a Morse Character from User ; Value returned in AH ; Uses AL, DH ;=========================================== GETCW CLRF AH ; Start with empty frame MOVLW 08H ; Init bit count MOVWF AL MOVLW GC_TO_L ; Init timeouts MOVWF DELAYLO MOVLW GC_TO_M ; Init timeouts MOVWF DELAYHI MOVLW GC_TO_H ; Init timeouts MOVWF DH GCLOOP BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL SAMPLE BSF STATUS, PC9 ; Set PAGE bit back for high access BTFSS PROCLAT, DITPDL ; Both Dit GOTO GCSINGLE BTFSC PROCLAT, DAHPDL ; and Dah ? GOTO GCTOGGLE GCSINGLE BTFSC PROCLAT, DITPDL ; DIT Paddle closed when = 1 GOTO GCDIT BTFSC PROCLAT, DAHPDL ; DAH Paddle closed when = 1 GOTO GCDAH DECFSZ DELAYLO, F ; Inner loop test GOTO GCLOOP MOVLW GC_TO_L ; Reset inner loop MOVWF DELAYLO DECFSZ DELAYHI, F ; Middle loop Test GOTO GCLOOP MOVLW GC_TO_M ; Reset middle loop MOVWF DELAYHI DECFSZ DH, F ; Outer loop Test GOTO GCLOOP GOTO GCJUST ; Timed out, we are done, now justify GCTOGGLE BTFSS MODEREG, IAM_A GOTO GCTOGL1 MOVLW PROCMASK ; Mode A, kill any in latch ANDWF PROCLAT, F GCTOGL1 BTFSC PROCLAT, OUTLAST ; If Dit was last send a DAH GOTO GCDAH GCDIT BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL DITOUT BSF STATUS, PC9 ; Set PAGE bit on return BCF STATUS, C ; DIT is 0 GOTO GCLOAD GCDAH BCF STATUS, PC9 ; Clear PAGE bit for low page call CALL DAHOUT BSF STATUS, PC9 ; Set PAGE bit on return BSF STATUS, C ; DAH is 1 GCLOAD RLF AH, F ; Shift left 1, CARRY->LSB DECFSZ AL, F ; Decrement bit count GOTO GCLD1 GOTO GCERR ; Overflow, treat as null GCLD1 MOVLW GC_TO_L ; Restart timeouts MOVWF DELAYLO MOVLW GC_TO_M ; MOVWF DELAYHI MOVLW 1 ; set a shorter 2nd timeout MOVWF DH GOTO GCLOOP GCJUST BSF STATUS, C ; Add tag bit GOTO GCJ2 GCJ1 BCF STATUS, C ; Fill in bottom GCJ2 RLF AH, F ; with zeroes DECFSZ AL, F ; Keep going till left justified GOTO GCJ1 GCDONE BCF STATUS, PC9 ; Clear PAGE bit for low page return RETLW 0 GCERR MOVLW M_TO ; Overflow error treated as a timeout MOVWF AH GOTO GCDONE ;=========================================== ; Convert CW Character Into Table Index ; Input: CW in AH ; Return: Index in AH ; Uses: AL, AH ; Note: A timeout code (M_TO) will be handled ; the same as an illegal code, .ie it won't ; be found in the table. ;=========================================== CW2IDX MOVF AH, W ; Transfer AH to AL MOVWF AL MOVLW M_A ; A ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 0 MOVLW M_I ; I ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 01D MOVLW M_K ; K ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 02D MOVLW M_L ; L ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 03D MOVLW M_S ; S ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 04D MOVLW M_T ; T ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 05D MOVLW M_U ; U ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 06D MOVLW M_X ; X ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 07D BCF PROCLAT, CONVERSE ; End converse mode MOVLW M_D ; D ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 08D MOVLW M_W ; W ? SUBWF AL, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 09D RETLW 10D ; Unknown command ;=========================================== ; Convert to ONES ; Valid numbers are returned with MSB set, ; Input: AH Output:W ;=========================================== ; CONV_LO MOVLW M_1 ; 1 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 01D+80H MOVLW M_2 ; 2 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 02D+80H MOVLW M_3 ; 3 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 03D+80H MOVLW M_4 ; 4 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 04D+80H MOVLW M_5 ; 5 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 05D+80H MOVLW M_6 ; 6 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 06D+80H MOVLW M_7 ; 7 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 07D+80H MOVLW M_8 ; 8 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 08D+80H MOVLW M_9 ; 9 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 09D+80H ; fall thru to pick up 0 or T ; or error ;=========================================== ; Convert to TENs ; Input: AH Output:W ;=========================================== ; CONV_HI MOVLW M_T ; T for 0 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 00D+80H MOVLW M_0 ; 0 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 00D+80H MOVLW M_1 ; 1 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 10D+80H MOVLW M_2 ; 2 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 20D+80H MOVLW M_3 ; 3 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 30D+80H MOVLW M_4 ; 4 ? SUBWF AH, W ; Compare BTFSC STATUS, Z ; Skip if no compare RETLW 40D+80H RETLW 00D ; Error for value > 49 or non-numeric ;********************************************************************* ; DECLARATIONS DE LA ZONE EEPROM ;********************************************************************* org 0x2100 ; adresse début zone eeprom DE M_C DE M_Q DE M_SP DE M_B DE M_A DE M_L DE M_I DE M_S DE M_E DE M_SP DE M_2 DE M_3 DE M_C DE M_M DE M_SP DE M_D DE M_E DE M_SP DE M_F DE M_1 DE M_P DE M_D DE M_X DE M_SP DE M_L DE M_O DE M_C DE M_SP DE M_J DE M_N DE M_0 DE M_8 DE M_X DE M_S DE M_SP DE M_D DE M_E DE M_P DE M_7 DE M_8 DE M_SP DE M_A DE M_N DE M_T DE M_SP DE M_D DE M_I DE M_P DE M_SP DE M_P DE M_W DE M_R DE M_SP DE M_5 DE M_W DE M_SP DE M_SP DE M_C DE M_Q DE M_SP DE M_B DE M_A DE M_L DE M_I DE M_S DE M_E DE M_SP DE M_2 DE M_3 DE M_C DE M_M DE M_SP DE M_D DE M_E DE M_SP DE M_F DE M_1 DE M_P DE M_D DE M_X DE M_SP DE M_L DE M_O DE M_C DE M_SP DE M_J DE M_N DE M_0 DE M_8 DE M_X DE M_S DE M_SP DE M_D DE M_E DE M_P DE M_7 DE M_8 DE M_SP DE M_A DE M_N DE M_T DE M_SP DE M_D DE M_I DE M_P DE M_SP DE M_P DE M_W DE M_R DE M_SP DE M_5 DE M_W DE M_END END