ZL1HIT Hellschreiber / PIC Beacon
This is a
simple circuit that does a fair bit of work. The core of the circuit is my favourite IC -
the Microchip PIC 16F84 Microcontroller / RISC Microprocessor.This project was inspired by my new friend Murray, ZL1BPU. Murray is very interested in H.F. band "digital" modes. He and a group of others are responbile for resurrecting the 1927 Hellschreiber visually received "fuzzy" digital mode of communication. More information about Hellschreiber can be read here. I highly recommend you take a look. It's fascinating stuff to be sure. |
This project produces base band Hellschreiber "digital" output which directly keys an HF radio transmitter. In my case I simply connect the output transistor to the CW Key input on my ICOM IC-706 Mk II transceiver. Since this rig is electronically keyed it works very well for this task.
Here is a sample of the Hellschrieber sent by this project:
![]()
Following is the PIC MPASM Source Code for Version 0.03 of the Beacon:
;*************************************************************************
; Feld Hellschreiber / CW Beacon Base Band Beacon
; Author: Bryan J. Rentoul
; Date: 10-April-1999
;
; All rights reserved
;
; Description: This project was inspired, yeh "specced" even, by Murray,
; ZL1BPU. It purpose is to send a mixture of CW and Hellschreiber via
; a simple CW transmitter based around a 3.58 MHz Xtal oscillator in the
; classic style. This is an early version and lacks any "user friendly"
; interface for loading new messages etc.
;
; This version uses the 5x5 pixel all upper case font set provided
; by ZL1BPU. (Letters 'C', 'V' an 'O' slighly modified for clarity.
;
;
;*************************************************************************
include "p16c84.inc"
include "bits.inc" ; Bit processing Macros
;*** SET CONFIG FUSES ***
__CONFIG _CP_OFF & _WDT_OFF & _HS_OSC
;For Assembler PORTAbility
W EQU 0 ;For file,W
w EQU 0 ;For file,W
F EQU 1 ;For file,F
f EQU 1 ;For file,F
;*******************************************************************
;* REGISTER DECLARATIONS
;*******************************************************************
ind equ 0 ;0=pseudo-reg 0 for indirect (FSR)
RTCC equ 1 ;1=Real Time Clock/Counter
TMR0 equ 1 ;1=Timer0 (same as above)
PC equ 2 ;2=PC
STATUS equ 3 ;3=Status Reg
INTCON equ 0Bh
PCLATH equ 0Ah
;* Status reg bits
BIT B_C,0,STATUS ;Carry
BIT B_DC,1,STATUS ;Half carry
BIT B_Z,2,STATUS ;Zero
BIT B_PD,3,STATUS ;Power down
BIT B_TO,4,STATUS ;Timeout
BIT B_PA0,5,STATUS ;Page select (56/57 only)
BIT B_PA1,6,STATUS ;Page select (56/57 only)
BIT B_PA2,7,STATUS ;GP flag
;----
;* OPTION reg bits
BIT B_RBPU,7,1
;----
;* INTCON reg bits (defined in p16c84.inc)
BIT I_GIE,7,INTCON
BIT I_EEIE,6,INTCON
BIT I_T0IE,5,INTCON
BIT I_INTE,4,INTCON
BIT I_RBIE,3,INTCON
BIT I_T0IF,2,INTCON
BIT I_INTF,1,INTCON
BIT I_RBIF,0,INTCON
;--------------------------------
FSR equ 4 ;4=file select reg 0-4=indirect address
fsr equ 4 ;4=file select reg 0-4=indirect address
PORTA equ 5 ;5=port A I/O register (4 bits)
PORTB equ 6 ;6=port B I/O register
porta equ 5 ;5=port A I/O register (4 bits)
portb equ 6 ;6=port B I/O register
;--------
BIT RA0,0,PORTA
BIT RA1,1,PORTA
BIT RA2,2,PORTA
BIT RA3,3,PORTA
BIT RA4,4,PORTA
BIT RB0,0,PORTB
BIT RB1,1,PORTB
BIT RB2,2,PORTB
BIT RB3,3,PORTB
BIT RB4,4,PORTB
BIT RB5,5,PORTB
BIT RB6,6,PORTB
BIT RB7,7,PORTB
BIT PTT,0,PORTA ; PTT Output
BIT SPAREA1,1,PORTA
BIT SIDETONE,2,PORTA
BIT TIMECAP,3,PORTA ; Timer capacitor output
BIT TIMEIN,4,PORTA ; Timer cap. input
BIT TXOUT,0,PORTB
BIT FSK0,1,PORTB
BIT FSK1,2,PORTB
BIT FSK2,3,PORTB
BIT DXMODE,4,PORTB ; DX Mode input. Jumper to GND for DX mode enable
;--------
SAVEW equ 0Ch ; Used in INT routine to save W and STATUS register
SAVES equ 0Dh
FLAGS equ 0Eh ; Inputs status register
DL1 equ 0Fh ; Delay routine counters
DL2 equ 10h ; " " "
DL3 equ 11h ; " " "
INTCNT equ 12h ; Interrupt call counter to count 64 calls then reset
TONECNT equ 13h ; Tone counter using to toggle output every 8 int calls
COLCNT equ 14h ; Column counter
ROWCNT equ 15h ; Row counter
CURCOL equ 16h ; Current column data for rotating each bit into carry flag
CHAR equ 17h ; Character code to send (ASCII values from 32 to 95 OK)
TEMP equ 18h
TEMP1 equ 19h
TIMER equ 20h ; We use this as a free running timer incremented
; by the Int routine. It is for CW dot / dash timing etc
SAVE1 equ 21h ; Just another TEMP variable
SAVE2 equ 22h ;
OUTBUF equ 23h ; CW Output buffer
; 24h to 2B free
; Define some status bits (flags)
BIT BUSY,0,FLAGS ; 0 = Buffer awaits new character to send (1 = busy)
BIT F_SIDETONE,1,FLAGS
BIT F_TABLE,2,FLAGS ; 0 = Font Table 1, 1 = Font Table 2
BIT F_CW,3,FLAGS ; In CW mode (1) the Int does not toggle TXOUT
;*** Reset Vector ********************************************************
ORG 0000
reset
GOTO INIT
; =========================
; == INTERRUPT PROCEDURE ==
; =========================
ORG 0004
TIMERINT ; Interrupt routine called (17.5 * 5 * 60) times per second providing
; pixel timing (5 pixels/rows per column)
; Save W and Status
CLB I_T0IE ; Disable this interrupt
CLB I_T0IF ; clear the interrupt
movwf SAVEW ; save w reg in Buffer
swapf SAVEW, f ; swap it
swapf STATUS,w ; get status (swapped)
movwf SAVES ; and save it
movlw 18 ; Reset Counter to 18 (instead of around zero or so)
movwf TMR0 ; /
; Interrupt procedure propper starts here
decfsz TONECNT, f ; Time for side tone click?
goto INEXT1
movlw 4 ; Toggle the Sidetone output every 6 calls
movwf TONECNT ; /
SKBC F_SIDETONE ; ...but only if the F_SIDETONE flag is set
TBIT SIDETONE ;
INEXT1
decfsz INTCNT, f ; Time to send a dot pixel?
goto IEND ; Nope...
;-------
decf TIMER, f ; decrement out free runing timer used in DELAY function
;-------
movlw 60 ; Yes...
movwf INTCNT ; Reset INTCNT to 60
; Check the BUSY FLAG. If zero do nothing
BBC BUSY, IEND
movfw ROWCNT
BBS B_Z, IENDCOL ; If ROWCNT is zero then we are done
; Other wise send next bit
movfw CURCOL
rrf CURCOL, f ; Rotate bit into carry flag for testing
BBS B_C, ITONEON
BBS F_CW, INEXT2 ; Dont touch TXOUT or F_SIDETONE if in CW mode
CLB TXOUT ; Turn TX carrier OFF
CLB F_SIDETONE ; flag side tone to OFF
goto INEXT2
ITONEON
BBS F_CW, INEXT2 ; Dont touch TXOUT or F_SIDETONE if in CW mode
SEB TXOUT ; Turn TX carrier ON
SEB F_SIDETONE ; flag side tone to ON
INEXT2
decf ROWCNT, f ; Decrement row counter
goto IEND ; Exit the int routine
IENDCOL
CLB BUSY ; Clear the BUSY flag to indicate column sent
IEND
swapf SAVES,w ; get status (swapped back)
movwf STATUS ; and restore it
swapf SAVEW,w ; restore W reg (swapped back to restore Z,C,DC flags)
SEB I_T0IE ; Re-enable the interrupt
retfie
;---- END OF INTERRUPT ROUTINE ------
;------------------------------------
;*************************************************************************
;** INIT
;** Hardware reset entry point
;**
;*************************************************************************
INIT ;Power-on entry
;*************************************************************************
;** RESET
;** software reset entry point
;**
;*************************************************************************
RESET ;Soft reset
;Init ports
movlw 10h ; All Port A bits to outputs except RA4 (Timer Cap input)
tris PORTA
movlw 0F0h ; Initialize Port B I/O pins
movwf PORTB
movlw 0F0h ; Port B<0-3> Output, Port B<4-7> Input
tris PORTB
movlw b'00011001' ; 0 Turn ON Port B pullups
; 0 External Int triggers on falling edge
; 0 TOCS low (timer run from internal clock)
; 1 TOSE - Counter timer/counter triggers on rising edge
; 1 Prescaler switched OUT (to WDT) ...
; 110 ...with div. 64 (WDT) or 128 (TMR0)
option ; Store W in the Option register.
CLB BUSY ; Clear busy flag
CLB F_SIDETONE
CLB F_CW ; Start in Hell' mode
clrf COLCNT
clrf ROWCNT
; Set up the timer interrupt to give us the required 122.5 dots per second
; (17.5 columns per second with 7 pixel rows)
; We reset the counter to 18 each time to yield 240 counts per
; interrupt = (7.3728 / 4 / 240) = 7680 ints. per second.
; 7680 / (17.5 x 7 row pixels) = 62.69. So every 63rd call to the int. should
; send one pixel.
;
; NOTE: In practice it was found tat every 60th call produced the right timing!
movlw 6
movwf TONECNT ; Init tone counter reg. Toggle TXOUT every 6 calls to int.
movlw 60
movwf INTCNT ; Initialize the INTCNT counter reg.
SEB I_T0IE ; enable timer overflow interrupt
SEB I_GIE ; global interrupt enable
; The TIMERINT interrupt routine is now running
goto MAIN_LOOP
;====================
DELAY ; Arbitary delay routine for CW dash/dot timing or whatever
; Make a delay send one blank column
clrf CURCOL
call TXCOL
return
;-----------------------------------
;================================================
; Subroutines for various things shall go here..
;================================================
TXCOL ; Sets the Int routine up to send a column - 5 rows
; (bits stretched over usual 14 rows)
; The CURCOL variable hold the column dat to send
movfw CURCOL ; Save column data (the Int routine destroys it)
movwf SAVE1
movlw 6
movwf ROWCNT ; ROWCNT will process 5 rows (0 not processed)
SEB BUSY ; Setting this bit signals Int routine to send a column
LOOPBS BUSY ; sit in loop waiting for BUSY to clear
movfw SAVE1 ; Restore column data (in case we want to send it again)
movwf CURCOL
return
;----------------------
TXCHAR ; Send character in "W" using Hellscheiber
movwf TEMP
movlw 32 ; make the char code zero based
subwf TEMP,f ; and store in TEMP
; The FONT table is split into two parts. Characters 0 to 31 are in part 1
; The rest are in part 2 (FONTTAB2)
movlw 32 ; Is the character to send less than 32?
subwf TEMP, w ; Subtract W (32) from TEMP
BBS B_C, USETABLE2 ; Carry Flag is SET for a positive result (TEMP > 32)
USETABLE1
movlw HIGH FONTTAB1 ; Get the jump page right and stick
movwf PCLATH ; it in PCLATH as per the book :)
CLB F_TABLE ; Clear the F_TABLE flag to indicate FONTTAB1 below
goto SENDCHAR ; --> go lookup and send this character
USETABLE2
movlw 32 ; Subtract addition 32 to correct char offset for table 2
subwf TEMP, f ; /
movlw HIGH FONTTAB2 ; Load PCLATH to correct memory page is used
movwf PCLATH ; /
SEB F_TABLE ; Set the F_TABLE flag to indicate FONTTAB2 below
SENDCHAR
movfw TEMP ; Retrive char offset
addwf TEMP, f ; multiply by 5 to get to start of
addwf TEMP, f ; char in font table
addwf TEMP, f ; /
addwf TEMP, f ; /
clrf COLCNT ; Reset COLCNT (column counter)
TAB1LOOP
movfw TEMP ; Retrieve char font table offset
addwf COLCNT, w ; Add the column number
CBC F_TABLE, FONTTAB1 ; Call the right table based on F_TABLE flag ...
CBS F_TABLE, FONTTAB2 ; ...set above to get the column data
movwf CURCOL
call TXCOL ; Flag the Interrupt routine to send this column
SKBS DXMODE ; If no jumper on for DX mode then exit
call TXCOL ; otherwise send the column a 2nd time
incf COLCNT, f ; Increment COLCNT
movfw COLCNT ; Last column (5) ?
sublw 5
BBC B_Z, TAB1LOOP ; If not then send next column
clrf CURCOL ; Send one blank column
call TXCOL ; /
return
;--------------------------------
;--- CW ROUTINES START HERE -----
;--------------------------------
SENDDASH
SEB F_SIDETONE
SEB TXOUT ; PTT on
call DELAY ; Dash delay
call DELAY
call DELAY
call DELAY
call DELAY
CLB TXOUT ; PTT off
CLB F_SIDETONE
call DELAY
return
;-----------------------------------
SENDDOT
SEB F_SIDETONE
SEB TXOUT ; PTT on
call DELAY ; Dot delay
call DELAY
CLB TXOUT ; PTT off
CLB F_SIDETONE
call DELAY ; "space between" delay
return
;-----------------------------------
SENDCW ; Sends CW character held in W. RRF's each bit out until W = 1
; 00h in W means send space between words
SEB F_CW ; Tell the Int routine we are in CW mode
movwf OUTBUF
xorlw 0 ; Trigger Z flag (maybe)
BBS B_Z, SSPACE ; Its zero so just send a space
SILOOP
movfw OUTBUF
sublw 1
BBS B_Z, SIEND ; If just one then we are done
CLB B_C
rrf OUTBUF, f ; Get next bit to send
BBC B_C, SDOT ; Branch to DOT if Carry clear. Otherwise its a DASH
call SENDDASH
goto SILOOP ; loop back
SDOT
call SENDDOT
goto SILOOP ; loop back
SSPACE
call DELAY ; Send word space (just wait)
call DELAY
call DELAY
SIEND
call DELAY ; Inter char delay
call DELAY
call DELAY
CLB F_CW ; Tat's enough CW for now thanks :)
return
;------------------------------------
;********************************
;****** M A I N L O O P ******
;********************************
MAIN_LOOP
; Send the beacon string from the "T_BEACON" table
clrf TEMP1
BEACONL1
movlw HIGH T_BEACON ; Stick the MSByte of T_BEACON in PCLATH
movwf PCLATH
movfw TEMP1 ; Retrieve the beacon character offset
call T_BEACON ; Go get the character
xorlw 0 ; To update the Z flag
BBS B_Z, MLEND1 ; Zero? If so we are done so jump out to MLEND1
call TXCHAR ; Otherwise send this character
incf TEMP1, f ; Increment to next character
goto BEACONL1 ; Loopy de loop
MLEND1
call DELAY
call DELAY
;-----
; Same again but this time CW
clrf TEMP1
BEACONL2
movlw HIGH T_CALLSIGN ; Stick the MSByte of T_BEACON in PCLATH
movwf PCLATH
movfw TEMP1 ; Retrieve the beacon character offset
call T_CALLSIGN ; Go get the character
xorlw 0 ; To update the Z flag
BBS B_Z, MLEND2 ; Zero? If so we are done so jump out to MLEND1
movwf TEMP ; Save W
movlw HIGH CWT ; Get the dot and dashes from the CWT
movwf PCLATH ; /
movfw TEMP ; Restore W
call CWT ; /
call SENDCW ; Send this character
incf TEMP1, f ; Increment to next character
goto BEACONL2 ; Loopy de loop
MLEND2
call DELAY
call DELAY
goto MAIN_LOOP ; Luckily PIC MCU's do not get bored!
;-----------------------------------
ORG 0200h
FONTTAB1
addwf PC, f
retlw 0 ; 32 -
retlw 0
retlw 0
retlw 0
retlw 0
retlw 0 ; 33 - !
retlw 0
retlw 29
retlw 0
retlw 0
retlw 0 ; 34 - "
retlw 24
retlw 0
retlw 24
retlw 0
retlw 10 ; 35 - #
retlw 31
retlw 10
retlw 31
retlw 10
retlw 8 ; 36 - $
retlw 21
retlw 31
retlw 21
retlw 2
retlw 25 ; 37 - %
retlw 26
retlw 4
retlw 11
retlw 19
retlw 10 ; 38 - &
retlw 21
retlw 13
retlw 2
retlw 5
retlw 0 ; 39 - '
retlw 16
retlw 24
retlw 0
retlw 0
retlw 0 ; 40 - (
retlw 14
retlw 17
retlw 0
retlw 0
retlw 0 ; 41 - )
retlw 0
retlw 17
retlw 14
retlw 0
retlw 21 ; 42 - *
retlw 14
retlw 31
retlw 14
retlw 21
retlw 4 ; 43 - +
retlw 4
retlw 31
retlw 4
retlw 4
retlw 0 ; 44 - ,
retlw 1
retlw 6
retlw 0
retlw 0
retlw 4 ; 45 - -
retlw 4
retlw 4
retlw 4
retlw 4
retlw 0 ; 46 - .
retlw 3
retlw 3
retlw 0
retlw 0
retlw 1 ; 47 - /
retlw 2
retlw 4
retlw 8
retlw 16
retlw 14 ; 48 - 0
retlw 19
retlw 21
retlw 25
retlw 14
retlw 0 ; 49 - 1
retlw 16
retlw 31
retlw 0
retlw 0
retlw 3 ; 50 - 2
retlw 21
retlw 21
retlw 21
retlw 9
retlw 0 ; 51 - 3
retlw 21
retlw 21
retlw 21
retlw 10
retlw 0 ; 52 - 4
retlw 30
retlw 2
retlw 7
retlw 2
retlw 28 ; 53 - 5
retlw 21
retlw 21
retlw 21
retlw 2
retlw 14 ; 54 - 6
retlw 21
retlw 21
retlw 21
retlw 2
retlw 16 ; 55 - 7
retlw 16
retlw 23
retlw 24
retlw 0
retlw 10 ; 56 - 8
retlw 21
retlw 21
retlw 21
retlw 10
retlw 8 ; 57 - 9
retlw 21
retlw 21
retlw 21
retlw 14
retlw 0 ; 58 - :
retlw 5
retlw 5
retlw 0
retlw 0
retlw 0 ; 59 - ;
retlw 10
retlw 11
retlw 0
retlw 0
retlw 4 ; 60 - <
retlw 10
retlw 17
retlw 0
retlw 0
retlw 0 ; 61 - =
retlw 10
retlw 10
retlw 10
retlw 0
retlw 0 ; 62 - >
retlw 0
retlw 17
retlw 10
retlw 4
retlw 8 ; 63 - ?
retlw 16
retlw 21
retlw 20
retlw 8
;---------------------------------------
CWT ; CW Table Call with W=ASC of char to send (EG. 'A' (65) for "di'dah" )
addlw 0
SKBC B_Z ; Return a zero if zero received
retlw 0
movwf SAVE1 ; Calculate offset in table from ASCI char given
movlw 48 ; [ ASCII code for "0" ]
subwf SAVE1, w
addwf PC, f
retlw b'00111111' ; 0
retlw b'00111110' ; 1
retlw b'00111100' ; 2
retlw b'00111000' ; 3
retlw b'00110000' ; 4
retlw b'00100000' ; 5
retlw b'00100001' ; 6
retlw b'00100011' ; 7
retlw b'00100111' ; 8
retlw b'00101111' ; 9
retlw b'00000000' ; :
retlw b'00000000' ; ;
retlw b'00000000' ; <
retlw b'00000000' ; =
retlw b'00000000' ; >
retlw b'00000000' ; ?
retlw b'00000000' ; @
retlw b'00000110' ; A
retlw b'00010001' ; B
retlw b'00010101' ; C
retlw b'00001001' ; D
retlw b'00000010' ; E
retlw b'00010100' ; F
retlw b'00001011' ; G
retlw b'00010000' ; H
retlw b'00000100' ; I
retlw b'00011110' ; J
retlw b'00001101' ; K
retlw b'00010010' ; L
retlw b'00000111' ; M
retlw b'00000101' ; N
retlw b'00001111' ; O
retlw b'00010110' ; P
retlw b'00011011' ; Q
retlw b'00001010' ; R
retlw b'00001000' ; S
retlw b'00000011' ; T
retlw b'00001100' ; U
retlw b'00011000' ; V
retlw b'00001110' ; W
retlw b'00011001' ; X
retlw b'00011101' ; Y
retlw b'00010011' ; Z
;=========================
; FONT TABLE 2 (second half)
ORG 0300h ; start table at even multiple of 256 address
FONTTAB2
addwf PC, f
retlw 14 ; 64 - @
retlw 17
retlw 21
retlw 21
retlw 8
retlw 15 ; 65 - A
retlw 20
retlw 20
retlw 20
retlw 15
retlw 31 ; 66 - B
retlw 21
retlw 21
retlw 21
retlw 10
retlw 14 ; 67 - C
retlw 17
retlw 17
retlw 17
retlw 0 ;was 10
retlw 31 ; 68 - D
retlw 17
retlw 17
retlw 17
retlw 14
retlw 31 ; 69 - E
retlw 21
retlw 21
retlw 21
retlw 17
retlw 31 ; 70 - F
retlw 20
retlw 20
retlw 20
retlw 16
retlw 14 ; 71 - G
retlw 17
retlw 17
retlw 19
retlw 10
retlw 31 ; 72 - H
retlw 4
retlw 4
retlw 4
retlw 31
retlw 0 ; 73 - I
retlw 17
retlw 31
retlw 17
retlw 0
retlw 2 ; 74 - J
retlw 17
retlw 31
retlw 16
retlw 0
retlw 31 ; 75 - K
retlw 4
retlw 10
retlw 17
retlw 0
retlw 31 ; 76 - L
retlw 1
retlw 1
retlw 1
retlw 0
retlw 31 ; 77 - M
retlw 8
retlw 4
retlw 8
retlw 31
retlw 31 ; 78 - N
retlw 8
retlw 4
retlw 2
retlw 31
; retlw 31 ; 79 - O
; retlw 17
; retlw 17
; retlw 17
; retlw 31
retlw 14 ; 79 - O
retlw 17
retlw 17
retlw 17
retlw 14
retlw 31 ; 80 - P
retlw 20
retlw 20
retlw 20
retlw 8
retlw 31 ; 81 - Q
retlw 17
retlw 21
retlw 19
retlw 15
retlw 31 ; 82 - R
retlw 20
retlw 22
retlw 21
retlw 9
retlw 8 ; 83 - S
retlw 21
retlw 21
retlw 21
retlw 2
retlw 16 ; 84 - T
retlw 16
retlw 31
retlw 16
retlw 16
retlw 31 ; 85 - U
retlw 1
retlw 1
retlw 1
retlw 31
; retlw 16 ; 86 - V
; retlw 8
; retlw 4
; retlw 2
; retlw 31
retlw 24 ; 86 - V
retlw 6
retlw 1
retlw 6
retlw 24
retlw 30 ; 87 - W
retlw 1
retlw 6
retlw 1
retlw 30
retlw 17 ; 88 - X
retlw 10
retlw 4
retlw 10
retlw 17
retlw 16 ; 89 - Y
retlw 8
retlw 7
retlw 8
retlw 16
retlw 17 ; 90 - Z
retlw 19
retlw 21
retlw 25
retlw 17
retlw 31 ; 91 - [
retlw 17
retlw 17
retlw 0
retlw 0
retlw 16 ; 92 - \
retlw 8
retlw 4
retlw 2
retlw 1
retlw 17 ; 93 - ]
retlw 17
retlw 31
retlw 0
retlw 0
retlw 0 ; 94 - ^
retlw 8
retlw 16
retlw 8
retlw 0
retlw 31 ; 95 - _
retlw 31
retlw 31
retlw 31
retlw 31
T_BEACON
addwf PC, f
retlw ' '
retlw '>'
retlw '>'
retlw ' '
retlw 'H'
retlw 'E'
retlw 'L'
retlw 'L'
retlw 'S'
retlw 'C'
retlw 'H'
retlw 'R'
retlw 'E'
retlw 'I'
retlw 'B'
retlw 'E'
retlw 'R'
retlw ' '
retlw 'B'
retlw 'E'
retlw 'A'
retlw 'C'
retlw 'O'
retlw 'N'
retlw ' '
retlw 'V'
retlw 'I'
retlw 'A'
retlw ' '
retlw 'P'
retlw 'I'
retlw 'C'
retlw '1'
retlw '6'
retlw 'C'
retlw '8'
retlw '4'
retlw ' '
retlw 'M'
retlw 'C'
retlw 'U'
retlw ' '
retlw 'D'
retlw 'E'
retlw ' '
retlw 'Z'
retlw 'L'
retlw '1'
retlw 'H'
retlw 'I'
retlw 'T'
retlw ' '
retlw '<'
retlw '<'
retlw ' '
retlw ' '
retlw 0
T_CALLSIGN
addwf PC, f
retlw 'Z'
retlw 'L'
retlw '1'
retlw 'H'
retlw 'I'
retlw 'T'
retlw 0
END