RECEPTOR DE CONTROLE REMOTO GENERICO PARA HCS200…301 COM PIC 12F629 E 16F628A

GOSTARIA DE TER OPÇÃO DA FAMILIA PIC PARA SEU RECEPTOR DE CONTROLE REMOTO? ENTÃO ESTA RESOLVIDO !!! SEGUINDO O MESMO ESTILO DO ARTIGO “RECEPTOR DE CONTROLE REMOTO GENERICO PARA HCS200..301 COM AT89C2051” VOCE VERÁ AGORA MAIS 2 APLICAÇÕES GENÉRICAS BASEADAS NAS “ NOTE APLICATTIONS” DA MICROCHIP PUBLICADAS NA INTERNET.

PRIMEIRAMENTE, VAMOS VER UM RECEPTOR COM PIC16F628A COM 4 SAIDAS (LIGADAS A LEDS) QUE COMANDARÃO A SUA FUTURA APLICAÇÃO.
TUDO NO MESMO ESQUEMA. TEM QUE ESCOLHER A KEY (OU USAR A QUE FOI COMPILADA NO ARQUIVO HEX = ‘0123456789ABCDEF’) COMUM NO RECEPTOR E TRANSMISSOR. TEM QUE USAR DISC=SERIAL NUMBER . CONFORME O HCS USADO NO CHAVEIRINHO , TEM QUE PROGRAMAR A WORD ‘CONFIG’. VEJA SEMPRE OS DATASHETS DO COMPONENTE QUE SERÁ USADO.
NOVAMENTE, NÃO ESQUECER QUE A FREQUENCIA DO RECEPTOR TEM QUE SER IGUAL AO DO CONTROLE REMOTO (CHAVEIRINHO). LEIA OS ARTIGOS SOBRE ISTO NESTE BLOG. TUDO O ACIMA SE APLICA AOS DOIS CIRCUITOS.
A SEGUNDA APLICAÇÃO, MAIS SIMPLES, USA UM PEQUENO PIC12F629 PARA CONTROLAR 2 SAIDAS APENAS(LED1 E LED 2). MAS , GERALMENTE, É SUFICIENTE PARA A MAIORIA DAS APLICAÇÕES COMO ALARMES E CONTROLES REMOTOS DE PORTÃO. AMBOS OS ESQUEMA PERMITEM, POR DEFINIÇÃO DE PROGRAMA, MEMÓRIZAR ATÉ 15 CONTROLES (CHAVEIRINHOS).

EM RESUMO:
1) VOCE ADQUIRE UM PAR DE CONTROLE REMOTO (ESCOLHA 2 IGUAIS COM O MESMO CHIP HCS (PODE SER 200 , 201, 300 OU 301), DE QUALQUER FABRICANTE.
2) VOCE PROGRAMA UMA NOVA KEY (A SUA KEY OU A CITADA ACIMA SE FOR USAR O ARQUIVO COMPILADO) ,DEIXA AS OUTRAS WORDS TUDO ‘0000’
EXCETUANDO A WORD “CONFIG” QUE DEVE SER ‘ 0030’ PARA 200,300,301 E ‘0000’ PARA 201 . PODERÁ USAR O HCS_PROG OU HCS_PROG_TINY. NÃO PRECISA TIRAR O CHIP (GERALMENTE SMD) DA PLACA. APENAS, SOLDE PEQUENOS FIOZINHOS ( DO TIPO DE TELEFONE) E CONECTE NO PROGRAMADOR. AO FAZER ISTO , TIRE A BATERIA DE 12 VOLTS FORA DO CONTROLE. NÃO ESQUECA DE ATERRAR OS PINOS DA SAIDA S0 E S1.(VEJA DATASHEET DO HCS EM USO, NA SEÇÃO ‘PROGRAMAÇÃO’).
3) MONTE O CIRCUITO DO RECEPTOR (PROTOBOARD OU PLACA UNIVERSAL, DAQUELAS QUE VAI LIGANDO COM FIOZINHOS). PROGRAME AGORA O MICROCONTROLADOR COM O ARQUIVO HEX APROPRIADO. USE O WINPIC, IC_PROG OU QUALQUER OUTRO PROGRAMADOR COM UM HARDWARE COMO O JDM OU SIMILARES.
4) FAÇA O APRENDIZADO DE CADA CONTROLE , APERTANDO PRIMEIRO A TECLA ‘LEARN’ E DEPOIS ACIONANDO O CONTROLE REMOTO. FAÇA ISTO COM CADA CONTROLE. APÓS ISTO, EM USO, A CADA BOTÃO APERTADO NO CONTROLE REMOTO DEVERÁ ACENDER UM LED CORRESPONDENTE NO RECEPTOR.
5) AGORA , USE-O EM SEU NOVO PROJETO!!!

CHEGA DE BLÁ BLÁ BLÁ !!!

VEJA O ESQUEMA DA APLICAÇÃO 1 COM PIC 16F628A:

SEGUE ABAIXO ARQUIVO ASM PARA VERSÃO PIC16F628A:

DECPIC628A_ASM

SEGUE ABAIXO ARQUIVO HEX PARA VERSÃO PIC16F628A:

DECPIC628A_HEX

SEGUE ABAIXO, SUGESTÃO DE PLACA VERSÃO 16F628A :

placa_gen_628pdf

SEGUE ABAIXO, SUGESTÃO DE PLACA VERSÃO 16F628A COM COMPONENTES VISTO PELO LADO COBREADO E LISTA DE MATERIAIS:

placa_gen_628_montagem_pdf

VEJA O ESQUEMA DA APLICAÇÃO 2COM PIC 12F629:

SEGUE ABAIXO ARQUIVO ASM PARA VERSÃO PIC12F629:

DECPIC629_ASM

SEGUE ABAIXO ARQUIVO HEX PARA VERSÃO PIC12F629:

DECPIC629_HEX

SEGUE ABAIXO A PLACA PARA A VERSÃO 12F629:

placa_gen_629pdf

SEGUE ABAIXO A PLACA PARA A VERSÃO 12F629 COM COMPONENTES MONTADOS VISTO PELO LADO COBREADO E LISTA DE MATERIAIS:

placa_gen_629_montagem_pdf

ATÉ O PRÓXIMO ARTIGO !!!

Curiosidades:
Cuidado! Você está sendo vigiado!
O preço de se levar uma vida muito ‘corrida’
Sua cama e seu sono

Outros artigos:

Ensine valores morais aos seus filhos
Casais – Como administrar bem o dinheiro
Tenha um conceito correto sobre desastres naturais
Como sobreviver ao primeiro ano de casamento

99 comments on “RECEPTOR DE CONTROLE REMOTO GENERICO PARA HCS200…301 COM PIC 12F629 E 16F628A

  1. Caro Claudio, bom dia.
    Fico muito grato por sua atenção e retorno.
    Acredito que não haverá problemas em relação a quantidade de pinos do PIC12F629 (6 portas), pois tenho esse mesmo programa em .ASM para o PIC12C509. Imprimi sua resposta e vou tentar proceder as alterações e compilações. Para testar estou usando o simulador do Proteus 7. Apresenta um bom resultado. Segue abaixo o .ASM – Atenciosamente, Pretti

    ;**********************************************************************
    ; Pinout and notes on 16F628A K8 implementation imw/EWP
    ;
    ; 16F628A, 20 (or 4) MHz P-DIP part
    ; ==================================
    ; pin name connection
    ; ————————
    ; 1 RA2 N/C
    ; 2 RA3 N/C
    ; 3 RA4 N/C
    ; 4 MCLR 10k pullup resistor to +5v
    ; 5 VSS 0v (ground)
    ; 6 RB0 KEY(4.7 K res to base of oc trans keys tx)
    ; 7 RB1 N/C
    ; 8 RB2 ST (sidetone, to piezo spkr)
    ; 9 RB3 STK (beacon sw, optional, see manual)
    ; 10 RB4 DIT (paddle dit contact)
    ; 11 RB5 DAH (paddle dah contact)
    ; 12 RB6 N/C
    ; 13 RB7 PB (push button memory, hold 2 s for command)
    ; 14 VDD +5v (can be 3.5 to 5.5 v)
    ; 15 RA6 N/C
    ; 16 RA7 N/C
    ; 17 RA0 N/C
    ; 18 RA1 N/C
    ;
    ; A 0.01uF decoupling capacitor connects between pins 14 and 5.
    ;
    ; The unit can be 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).
    ;
    ;**********************************************************************
    ;
    TITLE “K8 Morse Keyer”
    LIST P=16F628A, R=D
    #include
    ;
    ;**********************************************************************
    ;***************** K8 Customization Header ****************************
    ;
    ; The following defines setup the basic configuration of the K8
    ; take a look at how they are used in the code to get an understanding
    ; of how they work. If you leave them as they are here you will get
    ; a working K8 without beacon mode, code protect off.
    ;
    ; If you don’t know what you are doing, only modify the
    ; CALL, SPEED_DEFAULT, message selection, and AR_ON
    ;
    ; DON’T CHANGE MODE_DEFAULT, PERIOD, or SPD_CONST
    ; unless you are thoroughly familiar
    ; with the code.
    ;——————————————————————-
    ;
    ; Define STANDARD, QRP, CQTEST, or CONTEST default operation
    #define STANDARD ; can be: STANDARD, QRP, CQTEST, or CONTEST
    ;
    ; If debug is defined, the code protect if OFF
    #define DEBUG ; default is defined
    ;
    ; If BEACON is defined, beacon mode enabled. See manual
    ; #define BEACON ; default is not defined
    ;
    ; Number of seconds for beacon repeat
    #define BEACON_TIME 4
    ;
    ; Defining NO_DE modifies default messages (EWP?)
    ; #define NO_DE
    ;
    ; Defining SETFARNS enables the Farnsworth option
    ; #define SETFARNS
    ;
    ; AR or or off in message, change as required
    #define AR_ON ; comment this out if you don’t want AR sent
    ;
    ; MODE_DEFAULT is a bitfield that sets the default mode
    ;
    ; Bit 0 = STEN, Sidetone enable, 1 is on
    ; Bit 1 = STRQ, Sidetone request, always 1 (EWP?)
    ; Bit 2 = always 0
    ; Bit 3 = SWAP paddles, 0 for normal
    ; Bit 4 = I_MODE, 1 for full IAMBIC mode, 0 DE mode (see manual)
    ; Bit 5 = IAM_A, Iambic Mode A flag (A=1, B=0)
    ; Bit 6 = SKM, Straight Key Mode enable when 1
    ; Bit 7 = ASP, Autospace enable bit, on when 1
    ;
    MODE_DEFAULT EQU 013H ; IAM_B mode + ASP off (original)
    ; MODE_DEFAULT EQU 0B3H ; IAM_A mode + ASP on (alternate)
    ;
    ;——————————————————-
    ; Morse Equates Table
    ; These need to be defined first but
    ; should NOT be changed
    ;
    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_CALL EQU 0x05 ; Toggle to USER Callsign
    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.
    ; These need to be defined first but should
    ; NOT be changed
    ;
    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
    ;
    ; ————Callsign default, change as required ————-
    ;
    CALL_0 EQU M_V ; Put your call here, letter by letter
    CALL_1 EQU M_A ; No more than 7 characters
    CALL_2 EQU M_3
    CALL_3 EQU M_E
    CALL_4 EQU M_P
    CALL_5 EQU M_END
    CALL_6 EQU M_END
    CALL_7 EQU M_END
    ;
    ; – Speed default, change as required to any defined WPM_** symbol —
    ;
    SPEED_DEFAULT EQU WPM_15 ; can be WPM_5 to WPM_49
    ;
    ;**************** End of Customization Header ***********************
    ;**********************************************************************
    ; Modify below this point only if you REALLY understand the code
    ;**********************************************************************
    ;**********************************************************************
    ; Configuration word selection, based on configuration parameters
    ;
    ifdef BEACON
    ifdef DEBUG
    ;development mode, code protect off, watchdog on
    ; __CONFIG _CP_OFF & _WDT_ON & _XT_OSC ; original EWP
    __CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _CP_OFF & _LVP_OFF & _BODEN_OFF
    else
    ;manufacture mode, code protect on, watchdog on
    ; __CONFIG _CP_ON & _WDT_ON & _XT_OSC ; original EWP
    __CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _CP_ON & _LVP_OFF & _BODEN_OFF
    endif
    else
    ifdef DEBUG
    ; manufacture mode, code protect off, watchdog off
    ; _lvp_off & _boden_off are required for strict ’84 compatibility EWP
    ; __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC; Original EWP
    __CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _BODEN_OFF
    else
    ;manufacture mode, code protect on, watchdog off
    ; __CONFIG _CP_ON & _WDT_OFF & _XT_OSC; Original EWP
    __CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _CP_ON & _LVP_OFF & _BODEN_OFF
    endif
    endif
    ;
    PERIOD EQU 120D
    SPD_CONST EQU 212D
    ;
    ;——————————————-
    ; 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, changed from ’84 X0C EWP
    PROCLAT ; Process Latch
    DELAYHI ; High delay counter register
    DELAYLO ; Low delay counter register
    TIMEBASE ; Morse Time Base
    CURMSG ; Currently Selected Message
    MODEREG ; Mode Bits
    WTEMP ; for loading PCLATH
    SPEEDIDX ; Current speed table index
    FARNS ; Letterspacing
    BX ;
    AL ; General Purpose Registers
    AH ;
    CL ;
    CH ;
    DL ;
    DH ;
    USRCALL: 8 ; User Callsign
    USERRAM: 16 ; 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
    I_MODE EQU 04H ; Full iambic mode enable (no auto DE message)
    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
    IAMBIT EQU 10H ; Use this to XORF I_MODE
    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
    ;
    ;*******************************************
    ; RESET ENTRY
    ;*******************************************
    ;
    ORG 0
    GOTO INIT
    ;
    ORG 4
    INTVEC GOTO INTVEC
    ;
    ;===========================================
    ; Subroutines
    ;
    ; Must reside in first
    ; 256 bytes of codespace
    ; (probably not necessary on the ‘628 EWP)
    ;===========================================
    ;===========================================
    ; Sample and Latch Input State
    ;===========================================
    ;
    SAMPLE
    CALL 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
    RETURN

    ;===========================================
    ; 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

    CALL NDITDELAY
    ifndef BEACON
    BCF GPIO, STK ; Unkey Sidetone
    endif
    BCF GPIO, KEY

    CALL 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
    CALL NDITDELAY
    DECFSZ BX, F
    GOTO NDLP

    ifndef BEACON
    BCF GPIO, STK ; Unkey Sidetone
    endif
    BCF GPIO, KEY

    CALL NDITDLY_NT

    ; if (I_MODE is off &&
    ; we are in conversational mode &&
    ; both paddles are pressed)
    ; then GOTO TRAILER

    BCF PROCLAT, DAHPDL ; Clear DAH bit in paddle register
    BCF PROCLAT, OUTLAST ; Mark that Dah was sent

    BTFSS MODEREG, I_MODE ; Is DE mode allowed ?
    BTFSC PROCLAT, CONVERSE ; Not in converse or iambic modes
    RETLW 0 ; No, return
    BTFSS GPIO, XDAHPDL
    BTFSC GPIO, XDITPDL ; if both paddles pressed goto TRAILER
    RETLW 0 ; else return

    ;===========================================
    ; Send Trailer Message
    ;===========================================

    TRAILER
    ; BCF PROCLAT, CONVERSE ; End conversational mode
    ifdef NO_DE
    CALL DITOUT ; Send rest of ‘K’
    CALL DAHOUT
    else
    CALL DITOUT ; Send rest of ‘D’
    CALL DITOUT
    endif
    CALL LETTERSPACE
    TRAIL0
    BTFSC GPIO, XDITPDL ; Wait till both paddles
    BTFSS GPIO, XDAHPDL ; are released
    GOTO TRAIL0
    MOVLW MSG7-MSGBASE ; Point to ‘DE’+1 message
    GOTO L_SENDMSG ; We will return to caller from there

    ;===========================================
    ; 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 10D ; 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-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
    ; RETURN 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
    ; RETURN 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. Extra letterspace
    ; is added via the value FARNS.
    ;===========================================

    WORDSPACE
    MOVLW 6
    GOTO WLSPAC0
    LETTERSPACE
    MOVLW 2
    WLSPAC0
    BTFSS PROCLAT, CONVERSE ; no extra space in converse mode
    ADDWF FARNS, W
    MOVWF BX
    WLSPAC1
    CALL 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.
    ;
    ; (This is probably not necessary for the ‘628
    ; but appears to work EWP)
    ;===========================================

    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.
    ;===========================================

    CONFIG
    ; ——- 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 LOADCALL ; C=Load new callsign
    GOTO FARNSWORTH ; F=Farnsworth Adjust
    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
    GOTO DE_TOGGLE ; Z=DE Mode Toggle
    ; ——- Allow Letterspace for below ———
    GOTO DUMPUSER ; D=Play user RAM
    GOTO MSGSELECT ; M=Message select
    GOTO PRACTICE ; P=Practice mode
    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
    WAKEUP
    MOVF BX, W ; Test beacon timer
    BTFSC STATUS, Z ; If zero: Beacon ON
    GOTO BEAC_ON
    DECF BX, F ; Else: Decrement beacon timer
    GOTO SERVICE ; 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
    endif

    ;*******************************************
    ;* MAIN GPIO Service Routine *
    ;*******************************************

    SERVICE
    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) && (I_MODE == TRUE)
    ; {
    ; 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
    BTFSS MODEREG, I_MODE ; DE mode ?
    GOTO CHK_SINGLE ; Yes: Skip
    ;
    ; 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 CONFIG ; Yes: do config routine
    PBMSG ; No: output message
    MOVF CURMSG, W ; Point to ‘current’ message
    GOTO QVRET

    ;===========================================
    ; 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 Extra Letterspace
    ; Uses: AH, CL
    ;===========================================

    FARNSWORTH
    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
    MOVLW 80H ; Validity check value
    MOVWF CL ; save it for later
    CALL L_CONV_LO ; Convert AH to 1’s
    BCF STATUS, PC9 ; Clear page bit
    ADDWF CL, W ; Check value
    BTFSS STATUS, C ; Valid if Carry = 1
    GOTO QUERET
    MOVWF FARNS ; Set new Farnsworth adj.
    GOTO COMRET_R

    ;===========================================
    ; 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
    ;===========================================

    MSGSELECT
    CALL SELDELAY
    MSGLOOP
    MOVLW MSG2-MSGBASE ; Select message 2: Short CQ
    MOVWF CURMSG
    MOVLW M_C ; C
    CALL OSCHAR
    MOVLW M_Q ; Q
    CALL OSCHAR
    CALL SELDELAY
    BTFSC CL, 0 ; Paddle closure ?
    GOTO MSGDONE ; Yes, all done

    MOVLW MSG1-MSGBASE ; Select message 1: Variable
    MOVWF CURMSG
    ifdef CQTEST
    MOVLW M_C ; C
    CALL OSCHAR
    MOVLW M_Q ; Q
    CALL OSCHAR
    MOVLW M_T ; T
    CALL OSCHAR
    endif
    ifdef CONTEST
    MOVLW M_T ; T
    CALL OSCHAR
    MOVLW M_S ; S
    CALL OSCHAR
    MOVLW M_T ; T
    CALL OSCHAR
    endif
    ifdef QRP
    MOVLW M_Q ; Q
    CALL OSCHAR
    MOVLW M_R ; R
    CALL OSCHAR
    MOVLW M_P ; P
    CALL OSCHAR
    endif
    ifdef STANDARD
    MOVLW M_C ; C
    CALL OSCHAR
    MOVLW M_Q ; Q
    CALL OSCHAR
    MOVLW M_L ; L
    CALL OSCHAR
    endif
    CALL SELDELAY
    BTFSC CL, 0 ; Paddle closure ?
    GOTO MSGDONE ; Yes, all done

    MOVLW MSG3-MSGBASE ; No, select message 3
    MOVWF CURMSG
    MOVLW M_D ; D
    CALL OSCHAR
    MOVLW M_X ; X
    CALL OSCHAR
    CALL SELDELAY
    BTFSC CL, 0 ; Paddle closure ?
    GOTO MSGDONE ; Yes, all done

    MOVLW MSG4-MSGBASE ; No, select message 4
    MOVWF CURMSG
    MOVLW M_C ; C
    CALL OSCHAR
    MOVLW M_Q ; Q
    CALL OSCHAR
    MOVLW M_C ; C
    CALL OSCHAR
    CALL SELDELAY
    BTFSC CL, 0 ; Paddle closure ?
    GOTO MSGDONE ; Yes, all done

    MOVLW MSG5-MSGBASE ; No, select message 5
    MOVWF CURMSG
    MOVLW M_M ; M
    CALL OSCHAR
    MOVLW M_S ; S
    CALL OSCHAR
    MOVLW M_G ; G
    CALL OSCHAR
    CALL SELDELAY
    BTFSS CL, 0 ; Paddle closure ?
    GOTO MSGLOOP ; No: loop till user picks one
    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

    ;===========================================
    ; Toggle DE Mode
    ; Toggle the user defined iambic enable
    ; In iambic mode, when both paddles are
    ; pressed alternating dits/dahs are sent.
    ; I_MODE off will allow the use of the
    ; automatic ‘DE’ message.
    ;===========================================

    DE_TOGGLE
    MOVLW IAMBIT
    XORWF MODEREG, F
    MOVLW M_D ; Assume DE mode
    BTFSC MODEREG, I_MODE
    MOVLW M_I ; Assumed wrong, it’s iambic mode
    GOTO COMRET ; Use shared return

    ;===========================================
    ; Code Practice
    ; Send a stream of random CW characters
    ;===========================================

    PRACTICE
    BSF STATUS, PC9 ; Set PAGE bit for long GOTO
    GOTO PRAC_RUN

    ;===========================================
    ; Load RAM Routine
    ; Two entry points, one for loading callsign
    ; and a second for loading user message.
    ;
    ; Uses AH, CL
    ;===========================================

    LOADCALL
    MOVLW USRCALL ; Point to start of callsign string
    MOVWF FSR
    MOVLW 08H ; Max callsign length (8 decimal)
    GOTO LU00
    LOADUSER

    MOVLW USERRAM ; Point to start of user string
    MOVLW 0FH ; Max string length (15 decimal)
    LU00
    MOVWF CL ; Length in reg CL
    ; CLRF INDF ; In case nothing is entered
    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
    ; 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 BCF STATUS, RP0 ;on ‘628 for ’84 compat on Bank0
    MOVLW H’07’ ;Init CMCON for digital IO
    MOVWF CMCON ;on PORTA
    ;
    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
    BTFSS STATUS, PD ; Is this is a power up or dead man WDT ?
    ; Finish reset if so
    GOTO WAKEUP ; else: Go do wakeup stuff
    else
    ; TODO – implement wake-on-interrupt
    endif
    ifdef SETFARNS
    MOVLW 1 ; CAN ONLY USE THIS IN NON_BEACON MODE !!!
    MOVWF FARNS
    else
    CLRF FARNS ; Initialize Farnsworth adj.
    endif
    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 USERRAM
    MOVWF FSR ; Use indirect addressing
    CLRF INDF ; To init user message empty
    MOVLW USRCALL ; Load callsign into RAM
    MOVWF FSR ; Use indirect addressing
    BSF STATUS, PC9 ; Set PAGE bit for long GOTO
    GOTO XFRSTUB
    XFRRET
    MOVLW MSG2-MSGBASE ; Select message 2: Short CQ
    MOVWF CURMSG ; as default
    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_G ; Send Version ID
    GOTO COMRET ; Use common return entry

    ;===========================================
    ; Page Boundary
    ;===========================================
    ORG 0300h ; change from 200 to make
    ; room for extra 628 code EWP
    ;===========================================
    ; 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
    MSG1
    ifdef CONTEST
    RETLW M_T ; Contest Message
    RETLW M_E
    RETLW M_S
    RETLW M_T
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_T ; Contest Message
    RETLW M_E
    RETLW M_S
    RETLW M_T
    RETLW M_SP
    RETLW M_END
    endif
    ifdef CQTEST
    RETLW M_C ; CQ TEST
    RETLW M_Q
    RETLW M_SP
    RETLW M_T
    RETLW M_E
    RETLW M_S
    RETLW M_T
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_T
    RETLW M_E
    RETLW M_S
    RETLW M_T
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_K
    RETLW M_END
    endif
    ifdef QRP
    RETLW M_C ; QRP
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    ; RETLW M_SP
    RETLW M_DN
    RETLW M_Q
    RETLW M_R
    RETLW M_P
    RETLW M_SP
    ifdef AR_ON
    RETLW M_AR
    RETLW M_SP
    endif
    RETLW M_K
    RETLW M_END
    endif
    ifdef STANDARD
    RETLW M_C ; Long CQ Message
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    endif
    ;
    MSG2
    RETLW M_C ; CQ Message
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    ; RETLW M_CALL ;extra
    ; RETLW M_SP ;extra
    ifdef AR_ON
    RETLW M_AR
    RETLW M_SP
    endif
    RETLW M_K
    RETLW M_END
    ;
    MSG3
    RETLW M_C ; CQ DX Message
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_C
    RETLW M_Q
    RETLW M_SP
    RETLW M_D
    RETLW M_X
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_D
    RETLW M_X
    RETLW M_SP
    ifdef AR_ON
    RETLW M_AR
    RETLW M_SP
    endif
    RETLW M_K
    RETLW M_END
    ;
    MSG4
    RETLW M_USER ; Custom Contest Message
    RETLW M_SP
    RETLW M_D
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    RETLW M_CALL
    RETLW M_SP
    ifdef AR_ON
    RETLW M_AR
    RETLW M_SP
    endif
    RETLW M_K
    RETLW M_END
    ;
    MSG5
    RETLW M_USER ; User Message
    RETLW M_SP
    RETLW M_END
    ;
    MSG7
    ifdef NO_DE
    RETLW CALL_1
    RETLW CALL_2
    RETLW CALL_3
    else
    RETLW M_E
    RETLW M_SP
    RETLW M_CALL
    endif
    RETLW M_SP
    RETLW M_END
    ;
    MSG8
    RETLW M_SP ; WPM suffix
    RETLW M_W
    RETLW M_P
    RETLW M_M
    RETLW M_END

    ;===========================================
    ; Randomized Code Practice Table
    ;===========================================

    GETPRAC
    MOVWF WTEMP
    MOVLW HIGH($)
    MOVWF PCLATH
    MOVF WTEMP,W
    ADDWF PC, F ; Jump through table
    CWTBL
    RETLW 0xE4 ; 8 8 1110 0100 = DAH-DAH-DAH-DI-DIT
    RETLW 0x20 ;25 I 0010 0000 = DI-DIT
    RETLW 0x70 ;39 W 0111 0000 = DI-DAH-DAH
    RETLW 0x50 ;34 R 0101 0000 = DI-DAH-DIT
    RETLW 0xA8 ;19 C 1010 1000 = DAH-DI-DAH-DIT
    RETLW 0xD8 ;33 Q 1101 1000 = DAH-DAH-DI-DAH
    RETLW 0x32 ;15 ? 0011 0010 = DI-DI-DAH-DAH-DI-DIT
    RETLW 0x3C ; 2 2 0011 1100 = DI-DI-DAH-DAH-DAH
    RETLW 0x94 ;16 / 1001 0100 = DAH-DI-DI-DAH-DIT
    RETLW 0x60 ;17 A 0110 0000 = DI-DAH
    RETLW 0x68 ;32 P 0110 1000 = DI-DAH-DAH-DIT
    RETLW 0xFC ; 0 0 1111 1100 = DAH-DAH-DAH-DAH-DAH
    RETLW 0x78 ;26 J 0111 1000 = DI-DAH-DAH-DAH
    RETLW 0xCE ;13 , 1100 1110 = DAH-DAH-DI-DI-DAH-DAH
    RETLW 0x0C ; 4 4 0000 1100 = DI-DI-DI-DI-DAH
    RETLW 0x10 ;35 S 0001 0000 = DI-DI-DIT
    RETLW 0x8C ;14 BT 1000 1100 = DAH-DI-DI-DI-DAH
    RETLW 0x54 ;10 AR 0101 0100 = DI-DAH-DI-DAH-DIT
    RETLW 0x28 ;22 F 0010 1000 = DI-DI-DAH-DIT
    RETLW 0xD0 ;23 G 1101 0000 = DAH-DAH-DIT
    RETLW 0x90 ;20 D 1001 0000 = DAH-DI-DIT
    RETLW 0x04 ; 5 5 0000 0100 = DI-DI-DI-DI-DIT
    RETLW 0x88 ;18 B 1000 1000 = DAH-DI-DI-DIT
    RETLW 0x18 ;38 V 0001 1000 = DI-DI-DI-DAH
    RETLW 0xF4 ; 9 9 1111 0100 = DAH-DAH-DAH-DAH-DIT
    RETLW 0x56 ;12 . 0101 0110 = DAH-DAH-DI-DI-DAH
    RETLW 0xC0 ;36 T 1100 0000 = DAH
    RETLW 0x30 ;37 U 0011 0000 = DI-DI-DAH
    RETLW 0xB8 ;41 Y 1101 1000 = DAH-DAH-DI-DAH
    RETLW 0xE0 ;29 M 1110 0000 = DAH-DAH
    RETLW 0xC4 ; 7 7 1100 0100 = DAH-DAH-DI-DI-DIT
    RETLW 0x84 ; 6 6 1000 0100 = DAH-DI-DI-DI-DIT
    RETLW 0x16 ;11 SK 0001 0110 = DI-DI-DI-DAH-DI-DAH
    RETLW 0x1C ; 3 3 0001 1100 = DI-DI-DI-DAH-DAH
    RETLW 0x08 ;24 H 0000 1000 = DI-DI-DI-DIT
    RETLW 0x98 ;40 X 1001 1000 = DAH-DI-DI-DAH
    RETLW 0xC8 ;42 Z 1100 1000 = DAH-DAH-DI-DIT
    RETLW 0x48 ;28 L 0100 1000 = DI-DAH-DI-DIT
    RETLW 0x7C ; 1 1 0111 1100 = DI-DAH-DAH-DAH-DAH
    RETLW 0xB0 ;27 K 1011 0000 = DAH-DI-DAH
    RETLW 0xA0 ;30 N 1010 0000 = DAH-DIT
    RETLW 0x40 ;21 E 0100 0000 = DIT
    RETLW 0xF0 ;31 O 1111 0000 = DAH-DAH-DAH

    ;===========================================
    ; 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
    ; endif
    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_CALL ; Toggle to Callsign Mode ?
    SUBWF AL, W
    BTFSC STATUS, Z ; Skip if no compare
    GOTO S1CALL
    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
    S1CALL
    MOVLW USRCALL ; Point to start of callsign string
    GOTO S100
    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

    ;===========================================
    ; This was moved here from the low page to
    ; free up some code space. It’s function is
    ; to load fixed callsign into RAM.
    ;===========================================

    XFRSTUB
    MOVLW CALL_0
    CALL XFRIDX
    MOVLW CALL_1
    CALL XFRIDX
    MOVLW CALL_2
    CALL XFRIDX
    MOVLW CALL_3
    CALL XFRIDX
    MOVLW CALL_4
    CALL XFRIDX
    MOVLW CALL_5
    CALL XFRIDX
    MOVLW CALL_6
    CALL XFRIDX
    MOVLW CALL_7
    CALL XFRIDX
    BCF STATUS, PC9 ; Clear PAGE bit for low page goto
    GOTO XFRRET

    ;===========================================
    ; Code Practice
    ; Send a stream of random CW characters
    ;===========================================
    ;
    ; Generates pseudo random numbers by hopping
    ; though a morse character table with a constantly
    ; incrementing index. The index is incremented on
    ; every pass by an odd valued skip factor. A new
    ; skip factor is used periodically. The interval
    ; of the period is “randomly” read from the timer.
    ; Note that by virtue of the fact that the skip
    ; can cause a table bounds wrap, both positive
    ; and negative moves occur.
    ; DH = Skip, DL = Running index, AH = Loop count

    PRAC_RUN
    CLRF DL ; Clear index
    PRCLP0
    MOVF TMR0, W ; Get an initial skip factor
    MOVWF AH ; save loop count
    IORLW 01H ; make it odd
    ANDLW 01FH ; 1 -> 31
    MOVWF DH
    PRCLP1
    MOVF DH, W ; Get skip value in W
    ADDWF DL, F ; Add to index
    MOVLW -43D ; If > 42 wrap around
    ADDWF DL, W ; Carry will be set on wrap
    BTFSS STATUS, C ; Value wrapped, use wrapped value
    MOVF DL, W ; Value was within limits, use it as is
    MOVWF DL ; Update index in case we wrapped
    CALL GETPRAC
    BCF STATUS, PC9 ; Clear page bit for low page call
    CALL OSCHAR
    BSF STATUS, PC9 ; Set PAGE bit on return
    BTFSC GPIO, XDITPDL ; loop till paddle hit
    BTFSS GPIO, XDAHPDL ; loop till paddle hit
    GOTO PRCDONE ; paddle hit: leave
    DECFSZ AH, F ; decrement loop count
    GOTO PRCLP1 ; NZ: Continue
    GOTO PRCLP0 ; Z: Get new skip factor & loop count
    PRCDONE
    BCF STATUS, PC9 ; Clear page bit
    GOTO WAIT4OFF ; Common “wait then return”

    ;===========================================
    ; 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_C ; C ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 01D
    MOVLW M_F ; F ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 02D
    MOVLW M_I ; I ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 03D
    MOVLW M_K ; K ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 04D
    MOVLW M_L ; L ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 05D
    MOVLW M_S ; S ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 06D
    MOVLW M_T ; T ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 07D
    MOVLW M_U ; U ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 08D
    MOVLW M_X ; X ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 09D
    MOVLW M_Z ; Z ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 10D
    BCF PROCLAT, CONVERSE ; End converse mode
    MOVLW M_D ; D ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 11D
    MOVLW M_M ; M ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 12D
    MOVLW M_P ; P ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 13D
    MOVLW M_W ; W ?
    SUBWF AL, W ; Compare
    BTFSC STATUS, Z ; Skip if no compare
    RETLW 14D
    RETLW 15D ; 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

    1. Olá de novo João!
      Estudando o seu arquivo asm enviado, notei que você já começou a modificar algumas linhas. Tem como enviar sem modificações (original) ou indicar o link que você pegou? Tem esquema para entender o que realmente vai ser usado? Você disse que viu este arquivo na versão 12c509. Porque não compilar este asm com o cabeçalho do 12f629? Fica muito mais fácil!!!! Este arquivo, eu deduzo que se trata de um equipamento que envia mensagens em código morse, com opção de uso do manipulador manual. É isto mesmo? No que você pretende usar esta ‘raridade’? Você gosta de radioamadorismo ou pretende bolar alguma coisa diferente com isto?
      Claudio

  2. Caro Claudo, bom dia.
    O programa em Assembly a que me referi no comentário anterior é para emissão de sinais morse. Trata-se de um oscilador iâmbico que usamos no radioamadorismo na faixa de CW. A minha intensão é tão somente gravar o .HEX no novo microcontrolador, no caso o PIC12F629. Só que não entendo de programação em Assembly. Se puder me ajudar fico grato. Atenciosamente, João.

  3. Bom dia. Tenho um programa escrito em Assembly para o PIC16F628A e gostaria de modificar sua estrutura para, após compilação, gravar o .HEX no PIC12F629. Voce poderia me ajudar nessa tarefa? Possso mandar o .ASM para análise. Grato.

    1. Olá João! Gostaria de saber se a quantidade de pinos que será usado é compativel, pois o PIC 12f629 só tem 6 portas ,sendo uma que pode ser usado somente como entrada e 5 como io’s. Agora , se tiver de usar cristal externo, só ficará 3 io’s e uma entrada. Seria suficiente? Se for, não é dificil alterar para este modelo. Basta alterar os arquivos iniciais do pic e alterar as configurações de trisa/trisb por trisio. Todas as menções de ‘porta’ e ‘ portb’ tem que ser alterada para ‘gpio’. Para isto, abra o seu arquivo no editor do MPLAB e faça as alterações. Escolha que pinos irá ligar determinada carga ou será entrada. Acerte o ‘trisio’ para isto também. Depois, recompile, se der falha, corrija os eventuais erros e de novo. Quando conseguir compilar, monte o circuito no protoboard e carregue o arquivo hex. Teste. Se não funciona a contento, faça novas alterações e recompile. Depois de umas 500 carga de hex (brincadeira) vai funcionar.
      Se desejar, pode enviar via este comentario, seu arquivo asm que eu dou uma olhada.
      Claudio

  4. Boa noite Claudio,desculpe-me então infelizmente estou no projeto errado.Estou querendo analisar o projeto do RECEPTOR que decodifica o CHAVEIRINHO da ht com 16f628.

    1. Olá Genilson! Não se preocupe. Eu também confundo, pois são muito parecidos. Tem o projeto do receptor generico que usa o 12f629 com o ht6p20b. Basta trocar o tipo de microcontrolador no código asm e trocar a gpio/ trisio por porta /trisa e portb/trisb no pinos que desejar usar para saidas. Há,….não se esqueça de colocar o ‘movlw.7 /movwf cmcon’ para desligar os comparadores do 16f628. Depois é só recompilar para o 16f628 no Mplab-ide e usar o código Hex.
      Cláudio

  5. OLA CLAUDIO realmente quando pego teu codigo asm e compilo pelo MPLAB e está dando varios erros tentei corrigi alguns mais que é erros de digitação,porém eu não sei nada de assembler.Vc poderia dá uma olhada nesse erros por favor.Muito obrigado

    1. Olá Genilson! Qual a versão que está dando erro? Para PIC 16f628a ou 12f629? Voce sabe que este receptor precisa ter um HC200 .. 301 programado por voce para funcionar, não sabe? E que não ira funcionar com chaveirinhos comerciais a menos que voce reporograme seus Hc..?
      Claudio

Comments are closed.

Back To Top