PIC CW Keyer and Frequency Counter

This is a PIC keyer I wrote several years ago that can be used as an outboard keyer, internal to an existing rig, or as a CW beacon.  The code compiles for the Microchip 16F84, 16F84A, 16F628, 16F628A, and 16F648A running at 4 Mhz.  (VE3VXO has also ported this code to the PIC18 series.  Download here.)  It also includes a frequency counter suitable for use with homebrew rigs.  I no longer update this code as it’s been very stable and has all the features I needed at the time I developed it.  I have a new CW keyer I have developed for the Arduino platform which is largely based on this original effort with the PIC.

Features include:

  • 5 to 60 WPM speed
  • Three memories: one for a callsign, and two longer memories, all non-volatile
  • RF frequency counter
  • Frequency-adjustable sidetone or on-off keying for an external sidetone oscillator
  • Reverse paddle mode
  • Random code practice generator
  • Practice sending mode
  • Iambic A and B modes
  • Feature rich command mode
  • Modular code design and compile-time selectable features
  • 16F84, 16F84A, 16F628, 16F628A, 16F648A target devices (more possible due to modular code)
  • Beacon mode for CW beacons
  • Transmitter tuning mode
  • Straight key mode
  • Bug mode
  • In circuit programming with a computer serial port and free software enables quick code updates or easy experimentation

Schematics

Complete Schematic

Quick and Dirty Schematic

( Ignore the outdated email address and web page URL on these schematics.  These schematics were created with Eagle.  Sorry, I do not have PCB artwork.)

Connections

Schematic Name Type Function
DIT Input Connect to paddle; ground to activate dit (dot) keying
DAH Input Connect to paddle; ground to activate dah (dash) keying
TX_KEY Output This goes low (ground) on transmit and should be connected to the key input of the transmitter.
ST_KEY Output Sidetone key. This goes high (+12 volts) to supply voltage to an external sidetone oscillator.
SIDETONE Output Square wave sidetone output. Couple into an audio stage of the receiver.
TX_WAKE Output This output is grounded when the transmitter should be turned on. QSK rigs don’t need this – this is intended more for beacon mode and could be used to turn off the rig to conserve power during idle times. If the key is used in beacon mode as a VHF “fox” for a foxhunt, this line can key transmit on an FM rig and the sidetone line can feed the transmit audio with appropriate attenuation. This line could also be used to key a T/R relay on non-QSK rigs.
Programming Connector Input This can be connected to a serial port on a computer to do quick programming.
Freq Count Input Frequency counter input. Connect to a VFO to use the frequency counter functionality.

Note that the ST_KEY line does not supply a square or sine wave sidetone but rather a voltage to key an external oscillator.  For those who aren’t concerned with a pure sine wave sidetone and want to save parts, the SIDETONE line supplies a square wave signal suitable for coupling into a receiver audio stage. The square wave can be “softened” with a resistor and capacitor acting like a low pass filter if desired.  I have also successfully used a capacitor and the winding of a junkbox audio transformer as a low pass filter with good results. The SIDETONE line with the coupling capacitor also can drive an 8 ohm speaker.

The 1N4148 diodes can be substituted with 1N914 diodes or most any general purpose diode.

Operation

Upon power up, the unit says “HI” in CW. This does not key the TX_KEY or TX_WAKE line. (To disable this, comment out #define include_needless_feature_code in the code inclusion section)

When the unit is initially turned on, it will default to a speed of 20 WPM. When the mode button is pressed, the unit goes into command mode. In this mode, you place commands by sending a letter in CW. Here are the commands:

Command Description Comments
A Switch to Iambic A Mode
B Switch to Iambic B Mode
C CW Speed Calibration Check Expert Command
D Disable sidetone during transmit Expert Command
E Activate expert commands
F Adjust sidetone frequency Expert Command
H# Program Memory #
K###X Program frequency counter offset Expert Command
L Adjust frequency counter calibration Expert Command
M# Play Memory # (Does not transmit)
P Receive Code Practice
Q Send Code Practice
R Reverse paddles mode
S CW speed adjust mode
T Tuning mode
U Bug mode
V0 Deactive frequency counter offset Expert Command
V1 Frequency counter reading = offset – measurement Expert Command
V2 Frequency counter reading = measurement – offset Expert Command
V3 Frequency counter reading = measurement + offset Expert Command
W Weighting adjust mode
X Exit command mode
Y Announce frequency
Z Beacon test mode Does not transmit
? Query CW speed
, Toggle between three digit and eight digit frequency counter modes Expert command

If you send an unknown command, the PIC will send a question mark (?) in CW. When exiting command mode, the unit will emit a beep-boop (or a BK in CW if #define include_funky_tones_code is commented out). You can also exit command mode by hitting the mode button while in command mode.

A, B: Iambic A/B Modes

A and B commands switch between Iambic A and B modes. These commands are available if the code is compiled with #define include_iambic_b_code uncommented. If this code is not included, the keyer defaults to Iambic A behavior. (I’m not certain the iambic B code works well. Anyone who is familiar with iambic B, please test this and tell me what you think.)

C : Speed Calibration Check

This mode is used to verify or set the proper timing for CW sending. The keyer will send dits while the dit paddle is depressed. When released, the keyer will read back in CW the number of dits that were sent. The speed in words per minute equals the number of dots per minute divided by 25.

D: Disable Sidetone in Regular Mode

This turns the sidetone on and off in regular mode. It is intended for outboard keyer use with a transmitter that already has sidetone built in. In this instance, you would want to turn off the keyer sidetone during transmit so all you hear is the transmitter sidetone. During command mode, the keyer sidetone will continue to function.

F: Adjust Sidetone Frequency Mode

The keyer will turn on the sidetone. The dit paddle will increase the frequency and the dah paddle will lower it. Squeeze both paddles or hit the mode button to exit. The sidetone frequency is saved to EEPROM upon exit.

H#: Program Memory

# is the number of memory you want to program (0,1, or 2). After the units sounds a “boop beep”, use the paddle to program the memory with your CW. If you run out of space, the keyer will sound a continuous tone. To exit out of program mode, hit the mode switch. The transmitter is not keyed during programming. (requires #define include_m_and_h_cmd_code to be uncommented at compile time)

K###X: Program Frequency Counter Offset

This programs the offset for the the frequency counter (which is used only in three digit mode unders modes V1, V2, or V3). Subsitute ### with numbers. To terminate the command, send an X. A number up to 65,535 can be stored, but it’s likely you’ll only program a number from 000 to 1000. See details below for frequency counter operation and how to use the offset with your rig.

L: Frequency Counter Calibration Modes

This mode allows you to calibrate the frequency counter due to variations in PIC clock crystals. The frequency button can be pressed to announce the frequency in seven digit mode. The dit paddle increases the frequency readout, while the dah paddle decreases it. Hitting the mode button exits from the freuqnecy calibration mode. The calibration setting is non-volatile.

M#: Playback Memory

This simply plays back a memory without keying the transmitter. # = 0, 1, or 2 corresponding with the memory you want to play. (requires #define include_m_and_h_cmd_code to be uncommented at compile time)

P: CW Receive Practice Mode

This mode sends random cw in five character groups. Hit a button or paddle contact to exit. The transmitter is not keyed in this mode. ( #define include_cw_rx_practice_code must be uncommented at compile time to include this feature.)

Q: CW Send Practice Mode

This mode allows one to send code without transmitting. To exit, hit the mode button. ( #define include_cw_tx_practice_code must be uncommented at compile time to include this feature.)

R: Toggle Reverse Paddle Mode

This reverses the dit and dah contacts.

S: CW Speed Adjustment Mode

The keyer will send a string of dits. Pressing the dit paddle will increase the sending speed and the dah paddles decreases it. To exit speed mode, squeeze the paddles simultaneously or hit the mode button. Upon exiting speed mode, the keyer will send the speed in CW and save the setting to EEPROM. (Speed adjustment mode can also be activated by using the mode button hold shortcut below.)

T: Tuning Mode

The tuning mode is to be used in tuning up a rig or antenna tuner. Pressing the dit paddle will turn on the transmitter momentarily. Pressing the dah paddle will latch it on and off. To exit tuning mode, squeeze both paddles simultaneously, or hit the mode button. ( #define include_cw_tune_mode_code must be uncommented at compile time to include this feature.)

U: Bug Mode Toggle

This command turns bug mode on and off. In bug mode, the dit paddle works as normal, but the dah paddle acts like a straight key, emulating good old bug paddles for those folks who like them.

V0, V1, V2, V3: Frequency Counter Offset Mode

These commands configure the way the frequency that is announced in three digit mode is calculated.

V0 : Deactivate frequency counter offset

V1 : Frequency counter reading = offset – measurement

V2 : Frequency counter reading = measurement – offset

V3 : Frequency counter reading = measurement + offset

W: Adjust Weighting

In this mode, the weighting (the length ratio of dits to dahs, normally 1:3) can be adjusted. The dit contact decreases weighting and the dah contact increases it.

X: Exit Command Mode

Self explanatory.

Y: Readout Frequency

This command announces the current frequency in the current mode, either three or full seven/eight digits. The input frequency on pin 3 (RA4) is sampled for 250mS, and the internal resolution is 128 hertz. See the comma command below for more details.

#define include_freq_counter_code must be uncommented at compile time to include this code.

Z : Test Beacon Mode

This mode will play the beacon sequence once. Beacon mode is described further below.

? : Query Speed

The keyer sends in CW the current sending speed.

Comma : Frequency Counter Digits

This toggles the frequency counter between three digit and seven digit mode. In seven digit mode, all seven (or eight) counted digits are announced. In three digit mode only the 100, 10, and 1 kHz positions are announced with leading zeros truncated.

For example, in three digit mode:

7,040,000 Mhz would read 40

7,100,000 Mhz would read 100

7,000,000 Mhz would read 0

In seven digit mode:

7,040,000 would read 7040000

10,000,000 would read 10000000

Note that the resolution of the counter is 128 hertz, so often frequencies will not read exactly down to precisely zero hertz. In seven digit mode the offset (determined by the V1, V2, or V3 commands and the K command) is not applied. It is a straight reading.

Miscellaneous

If #define include_nnnnn_code is uncommented in the code, sending five Ns in a row will also put the keyer into command mode.  I found this feature to be problematic and turn it off in production chips, but if you’re lacking a momentary button switch, this feature may be used to remove the need for that switch.

Memories and Stuff

Function 0 button plays the callsign memory (memory 0), function 1 button plays memory 1, and function 2 button plays memory 2. A single short press will play a memory once. Holding the button down for about a half second and releasing it will put the keyer into memory repeat mode. Pressing any memory key, the mode button, or either of the dit or dah contacts will exit from a memory playback, either in one shot or repeat mode.

To program a memory, hold down the mode button and press the memory button of the memory you want to program. After releasing both buttons the unit will beep and you can program the memory. To exit program memory mode, hit the mode button.

Memory programming can also be done in command mode using the H command shown above.

If an attempt is made to play an empty memory, the keyer will sound a low beep (or will send three dits if include_funky_beeps_code is commented out).

[ #define include_function_button_code must be uncommented at compilation time to include this functionality ]

Beacon

On power up, if the dit line is grounded, the unit will go into beacon mode. This mode is intended to be used with a transmitter to create a HF or VHF beacon. The sequence repeats continuously as follows:

VVV DE <callsign> /B <memory 1> VVV DE <callsign> <memory 2> VVV DE <callsign>

The unit will stay in beacon mode forever, until the power is turned off. The beacon sequence can be customized in code if you desire a different message.

[ #define include_beacon_code must be uncommented at compilation time to include this functionality ]

Straight Key Mode

If the dah pin is grounded (dah paddle is depressed) or mode button is pressed upon power up, the unit will going into straight key mode. In this mode, the dit line can be connected to a straight key and the keyer acts like a simple code oscillator. The mode button will also function as a straight key in this mode and is intended for emergencies if you forgot to bring your paddle along. The keyer will stay in straight key mode until the power is turned off.

Reset to Defaults

If both the dit and dah contacts are grounded upon power up, the unit will reset to defaults when the paddles are released. This will clear out memories and reset the CW speed to its default (20 wpm).

Mode Button Shortcuts

Holding the mode button and pressing a memory button will place the unit into program memory mode. Holding the mode button and hitting the dit or dah contacts will immediately adjust the sending speed.

Call CQ Feature

Holding the Memory 0 button and depressing dit will cause the keyer to call CQ using the callsign programmed in Memory 0. #define include_call_cq_code must be uncommented at compilation time to include this feature.

PIC Programming

I have used DL4YHF’s WinPic software for programming PICs and Microchip’s MPLAB software for compiling code for years, however the author has stopped maintaining the code.  I’m in the process of switching over to WxPIC.  I used to use David Tait’s Free FPP software .  I can’t recall why I stopped using it, though it’s pretty old software now.

The DB-9 female connector on the schematic can be connected to the serial port on a computer for quick and easy programming. Using FPP, set the software for hardware “QandD (COM)” and set the appropriate COM port (usually COM1). Load the HEX file into FPP and click the program button. After closing S5, the programming switch, click OK in FPP. After about 40 seconds, the chip will be programmed. Open S5 and the chip should initialize. Note that this simple programming circuitry cannot be used to read the chip.  I have not been able to get the serial port programming to work with WinPic.

If you have problems getting PICs to program correctly in FPP, try backing off the timing, in particular increasing “Prog Cycle Delay” and “Power Up Delay” in the Setup window.

At the time of this writing FPP cannot program the 16F648A, therefore I use PP06 with a printer (parallel) port programmer to program them. You can find several free parallel port programmer designs on the web. FPP, PP06, and WinPic will handle just about any programmer you build. I built one with junkbox parts (TTL 7404 chip type) without having to buy anything. Any hobbyist who buys a programmer these days is crazy! :-) Parallel port programmers can read the flash memory, too, which is good for verifying that your programming was successful, or for duplicating chips.

If you have issues with WinPic aborting programming due to errors, change the process priority of WinPic to “realtime” in Windows.  This is done by right-clicking the task bar, selecting Task Manager, clicking the Processes tab, right-clicking the “WinPic.exe” process in the Image column, selecting Set Priority, Realtime.  Click “OK” in the warning dialog box that comes up.  Note that your cursor and your computer in general may become unresponsive when WinPic is programming a chip with the priority set to realtime as the operating system is giving WinPic all the CPU cycles it wants, right away.

Quick and Dirty Schematic

If you’re looking to build this quickly without all the bells and whistles, use the “Quick and Dirty” version above.

Recompile the code with the following code options commented out (place a semicolon in front of these lines):

#define include_function_button_code

#define include_freq_counter_code

#define include_freq_counter_button_code

Construction Notes

General

  • Most any reasonable NPN transistor can be for all the 2N2222 transistors in this circuit. 1N914 diodes can be used in place of the 1N4148s.

Frequency Counter

  • If you are installing this unit in a rig that has an IF that is a multiple of 4 Mhz, you may hear a beat note on the receiver caused by the PIC oscillator getting into the IF strip. If this occurs, reposition the PIC unit wiring to minimize the signal pickup. In one installation I was able to shift the PIC oscillator frequency out of the IF passband by removing C1 and C2. The oscillator and PIC still operated reliably, but your mileage may vary depending on the crystal. The 4 Mhz crystal undoubtedly could be replaced with a 3.57 Mhz one, although timing parameters in the code would need adjusted.
  • A low input signal level frequency counter circuit is provided. This can be used to couple to a VFO in a rig and provide minimal VFO frequency pulling. Experiment with C17 and use the lowest value necessary for reliable operation. This will minimize the load on the VFO circuit.
  • Adjust R19 so that the voltage on the collector of Q7 is around 2 to 2.5 volts with no signal.

Part Omissions

The following parts can be omitted if certain functionality is not required

Undesired Functionality Omit Parts Comments
In circuit programming DB9, R12, R13, D1, S5 Connect R2 directly to +5V
TX Wake line Q1, R8, C10 Only use is possibly in beacon mode
External sidetone oscillator keying Q2, Q4, R6, R7, C6 Most people will use the wave sidetone
Frequency Counter Q5, R15, C15, R14, S6, C16, D2, D3
Memories S2, S3, S4, C12, C13, C14 R9, R10, R11 can be removed as well if code is recompiled

I recommend buying parts from Mouser. I have always received good service from them and they have an excellent web interface. (I am not associated with Mouser other than being a satisfied customer.)

Source Code

There are various includes at the top of the code that can be commented out to omit certain features at compile time. I recommend buying 16F628 or 16F628A chips.

The 16F84 has only 1K of memory, so you’ll need to disable a lot of the features to make it fit on this chip. The entire code base does not fit on a 16F628, either, but you pretty much have to choose between beacon and code practice modes and the frequency counter. So, if you enable the frequency counter code, disable the beacon and code practice code, or vice versa, and it will fit.

Troubleshooting Hints

1.  Verify that all of your voltages are correct.  If something doesn’t look right, look for solder bridges between pins or pins that aren’t soldered at all.

2.  Do you have the chip inserted into the socket correctly?  Are any pins bent and not in the socket?

3.  Verify that the crystal is oscillating.  This can be done by connecting a high impedance oscilloscope or frequency counter to one of the pins of the crystal and observing a 4 Mhz signal.  You may also be able to hear the 4 Mhz oscillator on a radio receiver (perhaps couple the crystal to the antenna with a 10 pF capacitor?)  If your crystal is indeed a good crystal and you’ve double checked the wiring, but it still won’t oscillate, double check your configuration bits in your compiler and verify that you are not overriding the ones in code.  The Oscillator (“OSC”) field should read “HS”.

4.  Did you install all of the pull-up resistors needed for the particular functionality you compiled?  If the function button code is included and you don’t have the pull up resistors for those pins on the chip installed, the lines will be low and the PIC will think you are pressing a button.

5.  Did you compile for the right target chip?  Did the compilation complete successfully?  Are you using the right hex file on your hardrive?  (Note that there are some pre-compiled files at the bottom of this page you can try.)

6.  Verify the Configuration Bits in your compiler.  You should not be overriding the configuration bits set in code.

Support

Unfortunately I no longer support new features for this code, however you can visit the Radio Artisan discussion group to discuss this project.  (It’s been years since I touched assembly code and I’ve refocused my efforts on C programming on the Atmel AVR / Arduino platform.  Sorry.)

Source Code

Source code and various compiled binaries, and the VE3VXO PIC18 port of this code can be downloaded here, or you can simply copy and paste the code below.

; Keyer 3.0.25
; Goody K3NG
; 2008.12

; This is a PIC based keyer.  Currently supported devices are the 16F628, 16F628A, and 16F648A.  This will compile for the
; 16F84 and 16F84A, however I don't test these anymore.  The 16F628 doesn't cost much more, switch over to it!  :-)
;
; http://anthony.good.googlepages.com
; anthony.good at gmail.com

; SOFTWARE LICENSE ***************************************************************************************************************
;
; This software is free to use and distribute freely, as long as you include this software license text. You may not use
; this software or a derivative work in a commercial product without the written permission of the author. (I will normally
; approve kitting requests.)
;
; You may not distribute, compile, alter, update, or upload onto a target device this software if you have
; ever in an Internet forum or on the air insulted, chastised, harassed, or belittled any hams because they have not taken a code
; test or do not wish to do so.
;
; If you disagree with this license, you can request a full refund of the price you paid for the source code from me. If you agree
; with this license, you also promise to show new hams at an appropriate opportunity the magic of CW, in a friendly and helping way.
; You also promise to use your knowledge of CW for good and not to increase the size of your ego or be condescending to your fellow
; amateur radio operators.
;
; The author assumes no responsibility for the use or misuse of this software, nor makes any warranty as to its fitness for any
; particular purpose.
;
; ********************************************************************************************************************************

;	TODO - code practice, random callsigns
;	TODO - configurable memory repeat delay and store in eeprom
;	TODO - RX mute line (use TX wake line)
;	TODO - LCD Support
;	TODO - DDS Support
;	TODO - Configurable beacon sleep time
;	TODO - include_beacon_fox_message

; IN PROGRESS IN THIS VERSION: AUX and RIT/XIT outputs.  Halfway through enhanced memory support to support speed changes in memories

; 20080805 3.0.23 - fixed compile problem with include_weighting_code code
; 20080801 3.0.22 - Added big_memory_0 option

; ************************************** user configurable stuff **********************************************

; processor definition - uncomment one and be sure to set your compiler for the right proc
;#define processor_16f84
;#define processor_16f84a
#define processor_16f628
;#define processor_16f628a
;#define processor_16f648a

; ***************************
; *     code inclusion      *
; ***************************

; comment out these to reduce the code size or change features
; note that some modules of code depend on others; read the notes
; TODO: updated byte costs

; The Main Stuff

#define include_wavesidetone_code			; cost: ? bytes - additional support of wavesidetone
#define include_function_button_code		; cost: 160 bytes (includes some dependent code); support for buttons other than mode
#define include_toggle_sidetone_on_off_code	; cost: 18 bytes - code to turn off sidetone during TX
#define include_mode_button_hold_code		; cost: 43 bytes - code for mode button hold shortcuts (programming memories, quick speed adj)
#define include_funky_beeps_code			; cost: 17 bytes - code for high and low beeps using wave sidetone; not applicable if using externally keyed sidetone oscillator (requires wavesidetone_code)
;#define include_m_and_h_cmd_code			; cost: 86 bytes - command modes M and H: program and playback memories in command mode
;#define include_eis_mh_cmd_code				; instead of 0,1,2 for memory select, it's e,i,s

; Frequency Counter Code
#define include_freq_counter_code			; cost: 502 bytes - base frequency counter code
#define include_freq_counter_button_code	; cost: 24 bytes - support for frequency counter button (requires freq_counter_code, function_button_code)
;#define include_freq_counter_calib_code	; cost: 56 bytes - code for frequency calibration mode, command L (reqs freq_counter_code, function_button_code)

; Beacon Code
;#define include_beacon_code					; cost: 60 bytes - beacon mode support
;#define include_beacon_play_mem_1_code		; cost: ?? bytes - play memory 1 in beacon cycle
;#define include_beacon_play_mem_2_code		; cost: ?? bytes - play memory 2 in beacon cycle
;#define include_beacon_sleep_time
;#define include_beacon_qrss_code			; cost: ?? bytes - adds a qrss beacon ID during each cycle
;#define include_beacon_dual_ant_code		; TODO cost: ?? bytes - line RB7 controls antenna relay
;#define include_beacon_dual_tx				; TODO cost: ?? bytes - line RA3 switches TX each beacon cycle
;#define include_beacon_power_ctrl			; TODO cost: ?? bytes - RA0 and RA1 control TX power

; Optional Features
#define include_iambic_mode_code			; cost: 16 bytes - support for iambic a mode
;#define include_bug_code					; cost: 33 bytes - bug paddle mode
#define include_call_cq_code				; cost: 55 bytes - call cq mode
;#define include_nnnnn_code					; cost: 23 bytes - this code allows you to go into command mode after five consecutive Ns are sent
;#define include_txwake_line_code			; cost: ? bytes - code for TX_WAKE line
;#define include_tune_mode_code				; cost: 70 bytes - code for tune mode
;#define include_weighting_code				; cost: 77 bytes - command mode W: dah weighting
;#define include_paddle_reverse_code		; cost: 39 bytes - command mode R: reverse paddles
;#define include_needless_feature_code		; cost: 31 bytes - unimportant stuff
#define include_say_hi_code					; cost: ? bytes - say hi at startup
;#define include_calibration_code			; cost: 34 bytes - this adds another command mode for calibrating the speed counters
;#define include_cw_rx_practice_code		; cost: 113 bytes - command mode P: random five letter group code practice
;#define include_cw_tx_practice_code		; cost: 25 bytes - command mode Q: send code without transmitting
;#define big_memory_0						; cost: 0 bytes - make memory 0 (callsign) larger (48 CW elements instead of 28)

; Experimental / Under Construction
;#define include_aux_line_code				; cost: ? - UNDER DEVELOPMENT
;#define enhanced_memory_support				; cost: ? bytes - UNDER DEVELOPMENT
;#define include_9850_dds_code				; experimental / under construction
;#define include_serial_cw_keyboard			; experimental
;#define include_beacon_serial_ctrl_code	; cost: ? bytes - control the beacon using a serial port
;#define include_fox_code					; under construction

; Debug code - turn all of these off for production chips
;#define include_debug_code					; misc debug code - comment out for production chips
;#define include_serial_rx_debug_code
;#define exclude_delays_debug

; *********************************************
; * dependencies - do not change this section *
; *********************************************

	; include_serial_port_rx_code required by include_beacon_serial_ctrl_code, include_serial_cw_keyboard
	ifdef include_beacon_serial_ctrl_code
#define include_serial_port_rx_code
	endif
	ifdef include_serial_cw_keyboard
	ifndef include_serial_port_rx_code
#define include_serial_port_rx_code
	endif
	endif

	; include_cw_table_long required by include_serial_port_rx_code, include_cw_rx_practice_code
	ifdef include_serial_port_rx_code
#define include_cw_table_long
	endif
	ifdef include_cw_rx_practice_code
	ifndef include_cw_table_long
#define include_cw_table_long
	endif
	endif

	; include_rotateBCD012_left required by include_serial_port_rx_code, include_freq_counter_code
	ifdef include_serial_port_rx_code
#define include_rotateBCD012_left
	endif
	ifdef include_freq_counter_code
	ifndef include_rotateBCD012_left
#define include_rotateBCD012_left
	endif
	endif

	; include_beacon_qrss_code requires include_beacon_code
	ifdef include_beacon_qrss_code
	ifndef include_beacon_code
#define include_beacon_code
	endif
	endif

	; include_aux_line_code requires include_function_button_code
	ifdef include_aux_line_code
	ifndef include_function_button_code
#define include_function_button_code
	endif
	endif

; ***************************
; *    various settings     *
; ***************************

#define _4_mhz_clock
#define beacon_sleep_time_100ms d'1'		; the amount of time to wait in between beacon cycles, unit 100mS
#define beacon_qrss_speed d'1'				; beacon qrss id cw wpm
#define beacon_qrss_cycles d'3'				; qrss id after this many beacon cyles
#define initial_speed_wpm d'20'				; this is the default wpm speed when the cw_unit it first powered up
#define maxcwspeed  d'40'					; this is the max cw speed the user can increment to
#define initial_memory_repeat_delay d'35'	; this time in between repeating a memory in units of 100mS

; ***************************
; *    chip pin aliases     *
; ***************************

#define mode_switch	PORTB,0x00			; momentary switch
;					PORTB,0x01			; Serial Port RX
#define dit			PORTB,0x02			; dit of paddle
#define dah			PORTB,0x03			; dah of paddle
	ifndef include_wavesidetone_code
#define sidetone	PORTB,0x04			; keys external sidetone oscillator
	else
#define wavesidetone PORTB,0x04			; square wave sidetone output
	endif
#define key			PORTB,0x05			; transmitter cw key
	ifdef include_txwake_line_code
#define txwake		PORTB,0x06			; goes high to turn on TX if used as FM CW unit, or T/R relay
	endif

	ifdef include_function_button_code
#define switch0		PORTA,0x00
#define switch1		PORTA,0x01
#define switch2		PORTA,0x02
	endif

	ifdef include_aux_line_code
#define aux			PORTB,0x07
	endif

; *************** don't adjust anything below this line unless you know what the hell you are doing *************

; eeprom addresses - this defines eeprom memory locations
#define eeprom_addr_speed_wpm 0x00			; CW speed in binary format
#define eeprom_wavesidetone_setting 0x01	; sidetone frequency setting
#define eeprom_settings1_location 0x02		; various user settings, register eeprom_settings1
#define eeprom_freq_calib_high 0x03			; frequency counter calibration, high byte
#define eeprom_freq_calib_low 0x04			; frequency counter calibration, low byte
#define eeprom_freq_offset_high 0x05		; frequency counter offset, lower nibble is BCD2
#define eeprom_freq_offset_low 0x06			; frequency counter offset, upper nibble is BCD1, lower nibble is BCD0

	; first byte in memory location is a count of cw units stored in that memory
	; the remaining bytes are the stored cw units
	; a cw unit is two bits and is read MSB to LSB

	ifdef big_memory_0 ;---------------------

#define eeprom_memory_location_0 0x07		; cw memory 0 ( callsign )
#define eeprom_memory_loc_0_cw_units d'48'

	ifdef processor_16f84    ; Eeprom space for CW memories: 0x07 to 0x3F
#define eeprom_memory_location_1 0x14 	; cw memory 1
#define eeprom_memory_location_2 0x2A	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'84'				; four cw units can be stored per byte
#define eeprom_memory_loc_2_cw_units d'84'
#define processor_low_end
	endif ;processor_16f84

	ifdef processor_16f84a
#define eeprom_memory_location_1 0x14 	; cw memory 1
#define eeprom_memory_location_2 0x2A	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'84'				; four cw units can be stored per byte
#define eeprom_memory_loc_2_cw_units d'84'
#define processor_low_end
	endif ;processor_16f84a

	ifdef processor_16f628  	;eeprom space for CW memories: 0x07 to 0x7F
#define eeprom_memory_location_1 0x14	; cw memory 1
#define eeprom_memory_location_2 0x4A	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'212'
#define eeprom_memory_loc_2_cw_units d'212'
	endif ;processor_16f628

	ifdef processor_16f628a
#define eeprom_memory_location_1 0x14	; cw memory 1
#define eeprom_memory_location_2 0x4A	; cw memory 2  ( = eeprom_memory_location_1 + (eeprom_memory_loc_1_cw_units / 4) + 1 )
#define eeprom_memory_loc_1_cw_units d'212'
#define eeprom_memory_loc_2_cw_units d'212'
	endif ;processor_16f628a

	ifdef processor_16f648a
#define eeprom_memory_location_1 0x14	; cw memory 1
#define eeprom_memory_location_2 0x4A	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'212'
#define eeprom_memory_loc_2_cw_units d'212'
	endif ;processor_16f648a

	else ;big_memory_0 --------------------

#define eeprom_memory_location_0 0x07		; cw memory 0 ( callsign )
#define eeprom_memory_loc_0_cw_units d'28'

	ifdef processor_16f84
#define eeprom_memory_location_1 0x0F 	; cw memory 1
#define eeprom_memory_location_2 0x28	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'96'				; four cw units can be stored per byte
#define eeprom_memory_loc_2_cw_units d'92'
#define processor_low_end
	endif ;processor_16f84

	ifdef processor_16f84a
#define eeprom_memory_location_1 0x0F 	; cw memory 1
#define eeprom_memory_location_2 0x28	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'96'				; four cw units can be stored per byte
#define eeprom_memory_loc_2_cw_units d'92'
#define processor_low_end
	endif ;processor_16f84a

	ifdef processor_16f628
#define eeprom_memory_location_1 0x0F	; cw memory 1
#define eeprom_memory_location_2 0x48	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'224'
#define eeprom_memory_loc_2_cw_units d'220'
	endif ;processor_16f628

	ifdef processor_16f628a
#define eeprom_memory_location_1 0x0F	; cw memory 1
#define eeprom_memory_location_2 0x48	; cw memory 2  ( = eeprom_memory_location_1 + (eeprom_memory_loc_1_cw_units / 4) + 1 )
#define eeprom_memory_loc_1_cw_units d'224'
#define eeprom_memory_loc_2_cw_units d'220'
	endif ;processor_16f628a

	ifdef processor_16f648a
#define eeprom_memory_location_1 0x0F	; cw memory 1
#define eeprom_memory_location_2 0x48	; cw memory 2
#define eeprom_memory_loc_1_cw_units d'224'
#define eeprom_memory_loc_2_cw_units d'220'
	endif ;processor_16f648a

	endif ;big_memory_0

; timing calibrations - these must be adjusted for clock speed

	ifdef _4_mhz_clock
#define _100ms_counter1_calibration 0xff
#define _100ms_counter2_calibration 0x78
#define _100ms_counter3_calibration 0x01
#define _cw_unit_wpm_factor d'20000' 			; this adjustment control the time length of one CW unit ( a dit )
#define _beep_length d'20'
#define	_high_beep_tone d'20'
#define	_low_beep_tone d'55'
#define _initial_wavesidetone_setting d'34'  	; this is the default wavesidetone frequency (600Hz)
#define _frequency_counter_calib	d'48900'	; higher = shorter sampling duration (1/4 second)
												; delay = 15 * 1uS * (65535 - _frequency_counter_calib)
#define _txwake_delay_time d'30000'
#define _txwake_delay_time_straight_key d'400'

	endif ;_4_mhz_clock

	; processor declarations

	ifdef processor_16f84
	processor 16f84
	include &lt;p16f84.inc&gt;
	__config _HS_OSC &amp; _WDT_OFF
	#define cblock_start 0x0c
	errorlevel -312			; turn off nagging about device not needing page selection
	endif ;processor_16f84

	ifdef processor_16f84a
	processor 16f84a
	include &lt;p16f84a.inc&gt;
	__config _HS_OSC &amp; _WDT_OFF
	#define cblock_start 0x0c
	errorlevel -312			; turn off nagging about device not needing page selection
	endif ;processor_16f84a

	ifdef processor_16f628
	processor 16f628
	include &lt;p16f628.inc&gt;
	__config _HS_OSC &amp; _WDT_OFF &amp; _LVP_OFF &amp; _BODEN_OFF
	#define cblock_start 0x20
	errorlevel -312			; turn off nagging about device not needing page selection
	endif ;processor_16f628

	ifdef processor_16f628a
	processor 16f628a
	include &lt;p16f628a.inc&gt;
	__config _HS_OSC &amp; _WDT_OFF &amp; _LVP_OFF &amp; _BODEN_OFF
	#define cblock_start 0x20
	errorlevel -312			; turn off nagging about device not needing page selection
	endif ;processor_16f628a

	ifdef processor_16f648a
	processor 16f648a
	include &lt;p16f648a.inc&gt;
	__config _HS_OSC &amp; _WDT_OFF &amp; _LVP_OFF &amp; _BODEN_OFF
	#define cblock_start 0x20
	#define multi_memory_page_support
	endif ;processor_16f648a

	; define registers

	cblock cblock_start
		speed_wpm:1					;global
		bit_stuff1:1				;global
		bit_stuff2:1				;global
		bit_stuff3:1				;global
		cw_unit_counter:2			;loop_cw_unit
		counter1:1					;pause_100_ms, loop_cw_unit
		counter2:1					;pause_100_ms, loop_cw_unit
		counter3:1					;pause_100_ms, send_cw
		cw_char_to_send:2			;send_cw(parm)
		temp:1						;send_cw
		dividend:2					;divideby16bit
		divisor:2					;divideby16bit
		quotient:2					;divideby16bit
		remainder:2					;divideby16bit
		count:1						;divideby16bit
		binary2bcd_binary_in:2		;binary2bcd
		BCD0:1						;binary2bcd
		BCD1:1						;binary2bcd
		BCD2:1						;binary2bcd
		bcd_low_nibble:1			;speed_mode
		bcd_high_nibble:1			;speed_mode
		write_verify_data:1			;write_eeprom
		write_verify_addr:1			;write_eeprom
		command_mode_loop_count1:1	; check_mode_button (enhanced)
		command_mode_loop_count2:1	; check_mode_button (enhanced)
		command_mode_loop_count3:1	; check_mode_button (enhanced)
		command_mode_buffer1:1		; check_mode_button (enhanced)
		command_mode_buffer2:1		; check_mode_button (enhanced)
		recording_loop_counter1:1	; program_memory
		recording_loop_counter2:1	; program_memory
		cw_unit_count:1				; program_memory
		temp_memory:1				; program_memory
		temp_memory_eeprom_pointer:1 ;program_memory
		temp_memory_mask:1			; program_memory
		temp_w:1					; program_memory.store_temp_memory_to_eeprom
		send_cw_temp:1				; send_cw
		memory_number:1				; program_memory, play_memory
		eeprom_memory_locations:3	; program_memory, play_memory
		eeprom_memory_loc_limits:3	; program_memory
		start_of_memory_location:1	; program_memory, play_memory
		porta_temp:1				; check_function_buttons
		eeprom_settings1:1
		wavesidetone_counter
		wavesidetone_counter_setting
	endc

	ifdef enhanced_memory_support
		cblock
			cw_memory_element_count:1
			cw_element_number:1
			memory_location_element_number:1
			cw_character:1
		endc
	endif ;enhanced_memory_support

	ifdef include_txwake_line_code
		cblock
			txwakecounter:2
		endc
	endif ;include_txwake_line_code

	ifdef include_nnnnn_code
		cblock
			ditdah_history1:1			; global - include_nnnnn_code
			ditdah_history2:1			; global - include_nnnnn_code
			ditdah_history_timer1:1		; global - include_nnnnn_code
		endc
	endif

	ifdef include_weighting_code
		cblock
			cw_unit_counter_dah:2
			cw_unit_counter_dit:2
			speed_wpm_dah:1
		endc
	ifdef include_beacon_qrss_code
		cblock
			speed_wpm_dah_beacon_temp:1
		endc
	endif ;include_beacon_qrss_code

	endif ;include_weighting_code

	ifdef include_cw_rx_practice_code
		cblock
			randomnum:2
		endc
	endif

	ifdef include_freq_counter_code
		cblock
			freqcount:3
			BCD3:1
			frequency_counter_calib:2
			freq_offset_binary:2
			L_temp:1
			H_temp:1
		endc

bcd2binary_binary_out equ binary2bcd_binary_in

	ifdef include_debug_code
bcd2binary_binary_out_2 equ binary2bcd_binary_in+1	; for debugger work
freq_offset_binary_2 equ freq_offset_binary+1		; for debugger work
	endif ;include_debug_code

	endif ;include_freq_counter_code
		cblock
			beacon_cycle:1
		endc
	ifdef include_beacon_qrss_code

	endif ;include_beacon_qrss_code

#define dit_buffer					bit_stuff1,0x00			; used to store a dit in iambic operation
#define	dah_buffer					bit_stuff1,0x01			; used to store a dah
#define key_tx_active				bit_stuff1,0x02			; 0 = do not turn on key_tx line when sending cw
#define sending_dit					bit_stuff1,0x03			; 1 = send dit in send_dit_or_dah
	ifdef include_tune_mode_code
#define tune_latch_mode				bit_stuff1,0x04			; 1 = tx is latched on
	endif
#define temp_memory_dirty 			bit_stuff1,0x05			; used by memory record functions
#define paddle_was_hit				bit_stuff1,0x06
	ifdef include_function_button_code
#define memory_playback_manual_exit bit_stuff1,0x07
	endif

#define send_cw_preemptible			bit_stuff2,0x00			; send_cw routine can be interrupted by user when this flag is set
	ifndef include_wavesidetone_code
#define wavesidetone				bit_stuff2,0x01
	else
#define sidetone					bit_stuff2,0x01
	endif
#define expert_commands_on			bit_stuff2,0x02			; 1 = expert commands have been activated in command mode
#define bit_temp					bit_stuff2,0x03
	ifdef include_freq_counter_code
#define command_mode_v_cmd			bit_stuff2,0x04
#define command_mode_k_cmd			bit_stuff2,0x05
	endif
	ifdef include_call_cq_code
#define dit_hit						bit_stuff2,0x06
	endif ;include_call_cq_code
	ifdef include_m_and_h_cmd_code
#define command_mode_m_cmd			bit_stuff2,0x07

#define command_mode_h_cmd			bit_stuff3,0x00
	endif ;include_m_and_h_cmd_code

#define squeeze_detected			bit_stuff3,0x01

#define in_beacon_mode				bit_stuff3,0x02

;eeprom_settings1 is used for settings that need to be stored in nonvolatile memory
;they are written to eeprom using call write_eeprom_settings1
#define sidetone_off_during_tx		eeprom_settings1,0x00		; 1 = no sidetone during TX (for internal use in rigs that alread have sidetone)
	ifdef include_iambic_mode_code
#define iambic_b_mode				eeprom_settings1,0x01		; 1 = iambic b mode activated
	endif
	ifdef include_paddle_reverse_code
#define paddle_reverse				eeprom_settings1,0x02		; 1 = paddle reverse mode activated
	endif
	ifdef include_freq_counter_code
#define eight_digit_mode			eeprom_settings1,0x03		; 1 = 8 digit frequency counter mode
#define offset0						eeprom_settings1,0x04		; offset0 and 1 stores frequency counter offset mode (table below)
#define offset1						eeprom_settings1,0x05
	endif
	ifdef include_bug_code
#define bug_mode_on					eeprom_settings1,0x06		; 1 = bug mode activated
	endif

	; frequency counter offset table
	; offset0		offset1
	;	0				0		no offset
	;	0				1		reading = offset - measurement
	;	1				0		reading = measurement - offset
	;	1				1		reading = measurement + offset

	org 0x00
	pagesel main_program_start
	errorlevel -306
	goto main_program_start
	errorlevel +306

; Tables -------------------------------------------------------------

cw_table

	; this is a lookup table for sending numbers in CW using send_cw routine

	; 00 = termination
	; 01 = dit
	; 11 = dah

	; stream goes MSB -&gt; LSB  (MSB first)
	; two bytes long

	addwf PCL, F
	retlw b'11111111'	;0		0
	retlw b'11000000'
	retlw b'01111111'	;1		1
	retlw b'11000000'
	retlw b'01011111'	;2		2
	retlw b'11000000'
	retlw b'01010111'	;3		3
	retlw b'11000000'
	retlw b'01010101'	;4		4
	retlw b'11000000'
	retlw b'01010101'	;5		5
	retlw b'01000000'
	retlw b'11010101'	;6		6
	retlw b'01000000'
	retlw b'11110101'	;7		7
	retlw b'01000000'
	retlw b'11111101'	;8		8
	retlw b'01000000'
	retlw b'11111111'	;9		9
	retlw b'01000000'
	ifdef include_cw_table_long
	retlw b'01110000'	;A		10
	retlw b'00000000'
	retlw b'11010101'	;B		11
	retlw b'00000000'
	retlw b'11011101'	;C		12
	retlw b'00000000'
	retlw b'11010100'	;D		13
	retlw b'00000000'
	retlw b'01000000'	;E		14
	retlw b'00000000'
	retlw b'01011101'	;F		15
	retlw b'00000000'
	retlw b'11110100'	;G		16
	retlw b'00000000'
	retlw b'01010101'	;H		17
	retlw b'00000000'
	retlw b'01010000'	;I		18
	retlw b'00000000'
	retlw b'01111111'	;J		19
	retlw b'00000000'
	retlw b'11011100'	;K		20
	retlw b'00000000'
	retlw b'01110101'	;L		21
	retlw b'00000000'
	retlw b'11110000'	;M		22
	retlw b'00000000'
	retlw b'11010000'	;N		23
	retlw b'00000000'
	retlw b'01111101'	;P		24
	retlw b'00000000'
	retlw b'11110111'	;Q		25
	retlw b'00000000'
	retlw b'01110100'	;R		26
	retlw b'00000000'
	retlw b'01010100'	;S		27
	retlw b'00000000'
	retlw b'11000000'	;T		28
	retlw b'00000000'
	retlw b'01011100'	;U		29
	retlw b'00000000'
	retlw b'01010111'	;V		30
	retlw b'00000000'
	retlw b'01111100'	;W		31
	retlw b'00000000'
	retlw b'11010111'	;X		32
	retlw b'00000000'
	retlw b'11011111'	;Y		33
	retlw b'00000000'
	retlw b'11110101'	;Z		34
	retlw b'00000000'

	endif ;include_cw_table_long

; Subroutines -------------------------------------------------------------

check_paddles

	banksel PORTA
	pagesel $

	ifdef include_paddle_reverse_code
	btfsc paddle_reverse
	goto check_paddles_reverse
	endif ;include_paddle_reverse_code

	btfss dit		; if dit is not keyed, skip next
	bsf dit_buffer	; set dit_buffer
	btfss dah		; if dah is not keyed, skip next
	bsf dah_buffer	; set dah_buffer
    goto squeeze_check

	ifdef include_paddle_reverse_code
check_paddles_reverse
	btfss dit		; if dit is not keyed, skip next
	bsf dah_buffer	; set dah_buffer
	btfss dah		; if dah is not keyed, skip next
	bsf dit_buffer	; set dit_buffer
	endif ;include_paddle_reverse_code

squeeze_check
	ifdef include_iambic_mode_code
	btfsc iambic_b_mode				; don't do squeeze check if we're in iambic b mode
	return
	endif ;include_iambic_mode_code
	btfsc dit		; if both dit and dah are squeezed, set the squeeze_detected flag
	return
	btfsc dah
    return
    bsf squeeze_detected

    return

; ------------------

send_w_BCD_in_cw

	; this sends the lower nibble of w (BCD binary coded decimal) in CW

	movwf temp_memory
	addwf temp_memory, F 		; double it so we're in the right spot in cw_table
	movfw temp_memory			; put it in W for lookup table
	call cw_table
	movwf cw_char_to_send+0
	movfw temp_memory
	addlw 0x01					; add 1 to get next byte in table
	call cw_table
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	return

; ------------------

	ifdef include_funky_beeps_code

beep
	movwf temp_w						; store w temporarily
	movfw wavesidetone_counter_setting	; store real wavesidetone setting in temp_memory
	movwf temp_memory
	movfw temp_w						; get temp_w and put in for wavesidetone setting
	movwf wavesidetone_counter_setting

	movlw _beep_length
	movwf counter1
	movwf counter2

	bsf sidetone

beep_loop
	call produce_wavesidetone
	decfsz counter1, F
	goto beep_loop
	decfsz counter2, F
	goto beep_loop

	bcf sidetone

	movfw temp_memory					; restore real wavesidetone setting
	movwf wavesidetone_counter_setting
	return

; ------------------

high_beep
	ifndef exclude_delays_debug
	movlw _high_beep_tone
	call beep
	endif ;exclude_delays_debug
	return

; ------------------

low_beep
	ifndef exclude_delays_debug
	movlw _low_beep_tone
	call beep
	endif ;exclude_delays_debug
	return

; ------------------

beep_boop
	ifndef exclude_delays_debug
	call high_beep
	call low_beep
	endif ;exclude_delays_debug
	return

; ------------------

boop_beep
	call low_beep
	call high_beep
	return

	endif ;include_funky_beeps_code

; ------------------

divide16bit

	; dividend / divisor = quotient
	; this code from Mark Sullivan www.markworld.com (thanks for saving me hours of work ! :-)

	banksel PORTA
	pagesel $

	movlw d'16'
	movwf count
	clrf quotient+0	;quotient
	clrf quotient+1
	clrf remainder+0	;remainder
	clrf remainder+1
udiv1
	bcf	STATUS,C
	rlf	dividend+1,F
	rlf	dividend+0,F
	rlf	remainder+1,F
	rlf	remainder+0,F
	movf divisor+0,W
	subwf remainder+0,W
	btfss STATUS,Z
	goto udiv2
	movf divisor+1,W
	subwf remainder+1,W
	btfss STATUS,C
	goto udiv4
	movwf remainder+1
	movf divisor+0,W
	subwf remainder+0,F
	goto udiv3
udiv2
	btfss STATUS,C
	goto udiv4
	movf divisor+1,W
	subwf remainder+1,F
	btfss STATUS,C
	decf remainder+0,F
	movf divisor+0,W
	subwf remainder+0,F
udiv3
	bsf	STATUS,C
udiv4
	rlf	quotient+1,F
	rlf	quotient+0,F
	decfsz count,F
	goto udiv1
	retlw 0

; ------------------

	ifdef include_freq_counter_code

bcd2binary

	; BCD to 16 bit binary converter
	; adapted from Microchip AN526
	; http://www.microchip.com
	; thanks, Microchip !

	;input parameters:
	;bcd2binary+0 : MSB
	;bcd2binary+1 : LSB

	;output:
	;BCD0 = most significant digits
	;BCD1
	;BCD2 = least significant digits

	;each BCD register contains and upper and lower nibble BCD digit

	banksel PORTA
	pagesel $

	clrf bcd2binary_binary_out+0
	movf BCD0,W
	andlw 0x0F
	movwf bcd2binary_binary_out+1
	call mpy10a          ; result = 10a+b
	swapf BCD1,W
	call mpy10b          ; result = 10[10a+b]
	movf BCD1,W
	call mpy10b          ; result = 10[10[10a+b]+c]
	swapf BCD2,W
	call mpy10b          ; result = 10[10[10[10a+b]+c]+d]
	movf BCD2,W
	andlw 0x0F
	addwf bcd2binary_binary_out+1, F
	btfsc STATUS,C
	incf bcd2binary_binary_out+0, F       ; result = 10[10[10[10a+b]+c]+d]+e
	return               ; BCD to binary conversion done

	; subroutines

mpy10b
	andlw 0x0F
	addwf bcd2binary_binary_out+1, F
	btfsc STATUS,C
	incf bcd2binary_binary_out+0, F
mpy10a
	bcf STATUS,C        ; multiply by 2
	rlf bcd2binary_binary_out+1,W
	movwf L_temp
	rlf     bcd2binary_binary_out+0,W        ; (H_temp,L_temp) = 2*N
	movwf   H_temp
	bcf     STATUS,C        ; multiply by 2
	rlf     bcd2binary_binary_out+1, F
	rlf     bcd2binary_binary_out+0, F
	bcf     STATUS,C        ; multiply by 2
	rlf     bcd2binary_binary_out+1, F
	rlf     bcd2binary_binary_out+0, F
	bcf     STATUS,C        ; multiply by 2
	rlf     bcd2binary_binary_out+1, F
	rlf     bcd2binary_binary_out+0, F       ; (bcd2binary_binary_out+0,bcd2binary_binary_out+1) = 8*N
	movf    L_temp,W
	addwf   bcd2binary_binary_out+1, F
	btfsc   STATUS,C
	incf    bcd2binary_binary_out+0, F
	movf    H_temp,W
	addwf   bcd2binary_binary_out+0, F
	return              ; (bcd2binary_binary_out+0,bcd2binary_binary_out+1) = 10*N

	endif ;include_freq_counter_code

; ------------------

binary2bcd

	; 16 bit binary to bcd converter
	; adapted from Microchip AN526
	; http://www.microchip.com
	; thanks, Microchip !

	banksel PORTA
	pagesel $

	bcf     STATUS, C                ; clear the carry bit
	movlw   .16
	movwf   count
	clrf    BCD0		; most significant digit
	clrf    BCD1
	clrf    BCD2		; least significant digit
loop16
	rlf     binary2bcd_binary_in+1, F	; LSB
	rlf     binary2bcd_binary_in+0, F	; MSB
	rlf     BCD2, F
	rlf     BCD1, F
	rlf     BCD0, F
	decfsz  count, F
	goto    adjDEC
	retlw   0
adjDEC
	movlw   BCD2
	movwf   FSR
	call    adjBCD
	movlw   BCD1
	movwf   FSR
	call    adjBCD
	movlw   BCD0
	movwf   FSR
	call    adjBCD
	goto    loop16
adjBCD
	movlw   3
	addwf   INDF, W
	movwf   temp
	btfsc   temp,3          ; test if result &gt; 7
	movwf   INDF
	movlw   30
	addwf   INDF, W
	movwf   temp
	btfsc   temp,7          ; test if result &gt; 7
	movwf   INDF               ; save as MSD
	retlw   0

; ------------------

write_eeprom

	pagesel $

	; save EEDATA and EEADR for write verify
	errorlevel -302
	banksel EEDATA
	movfw EEDATA
	banksel PORTA
	movwf write_verify_data
	banksel EEADR
	movfw EEADR
	banksel PORTA
	movwf write_verify_addr
	errorlevel +302

start_write
	errorlevel -302
	banksel INTCON
	bcf	INTCON, GIE		; disable interrupts
	banksel EECON1
	bsf	EECON1, WREN	; enable write
	movlw 0x55			; required write sequence
	movwf EECON2		; required write sequence
	movlw 0xaa			; required write sequence
	movwf EECON2		; required write sequence
	bsf EECON1,WR 		; write
wait_to_complete_write
	btfsc EECON1, WR	; watch for WR flag to clear
	goto wait_to_complete_write

	; check if write failed, loop a couple times to try again
	banksel write_verify_addr
	movfw write_verify_addr	; select the address
	banksel EEADR
	movwf EEADR
	banksel EECON1
	bsf EECON1, RD		; read it
	banksel EEDATA
	movfw EEDATA		; get the data
	banksel PORTA
	subwf write_verify_data, W	; compare it
	btfss STATUS, Z		; skip next if its equal
	goto write_again	; otherwise, write it again

write_eeprom_blowout
	banksel INTCON
	bsf INTCON, GIE		; enable interrupts
	banksel EECON1
	bcf EECON1, WREN	; clear write enable flag
	bcf EECON1, EEIF	; clear EE Interrupt flag for the hell of it
	banksel PORTA
	return

write_again
	banksel write_verify_data
	movfw write_verify_data		; reload EEDATA and EEADR with the original values
	banksel EEDATA
	movwf EEDATA
	banksel write_verify_addr
	movfw write_verify_addr
	banksel EEADR
	movwf EEADR
	banksel PORTA
	goto start_write

	errorlevel -302

; ------------------
calculate_cw_unit_values

	banksel PORTA
	pagesel $

	movlw LOW _cw_unit_wpm_factor
	movwf dividend+1
	movlw HIGH _cw_unit_wpm_factor
	movwf dividend+0
	movfw speed_wpm
	movwf divisor+1		; lsb - divisor
	movlw 0x00
	movwf divisor+0		; msb - divisor

	call divide16bit
	movf	quotient+1, W	;load LSB of result
	movwf	cw_unit_counter+1
	movf	quotient+0, W	;load MSB of result
	movwf	cw_unit_counter+0

	ifdef include_weighting_code
	movlw LOW _cw_unit_wpm_factor
	movwf dividend+1
	movlw HIGH _cw_unit_wpm_factor
	movwf dividend+0
	movfw speed_wpm_dah
	movwf divisor+1		; lsb - divisor
	movlw 0x00
	movwf divisor+0		; msb - divisor

	call divide16bit
	movf	quotient+1, W	;load LSB of result
	movwf	cw_unit_counter_dah+1
	movf	quotient+0, W	;load MSB of result
	movwf	cw_unit_counter_dah+0
	endif ;include_weighting_code

	return

; ------------------
	ifdef include_mode_button_hold_code

	banksel PORTA
	pagesel $

wait_for_mode_switch_release
	btfss mode_switch
	goto wait_for_mode_switch_release
	call pause_100_ms
	call pause_100_ms
	bcf key_tx_active
	return

	endif

; ------------------

pause_100_ms

	banksel PORTA ; for good measure

	movlw _100ms_counter1_calibration
	movwf counter1
	movlw _100ms_counter2_calibration
	movwf counter2
	movlw _100ms_counter3_calibration
	movwf counter3

looplooploop
	ifndef exclude_delays_debug
	decfsz counter1, F
	goto looplooploop
	decfsz counter2, F
	goto looplooploop
	decfsz counter3, F
	goto looplooploop
	endif ;exclude_delays_debug
	return

; ------------------

pause_w_100ms

	; pause W * 100 milliseconds

	banksel PORTA	; for good measure

	movwf count
	ifndef exclude_delays_debug
loop_decrement
	call pause_100_ms
	decfsz count, F
	goto loop_decrement
	endif ;exclude_delays_debug
	return

; ------------------

produce_wavesidetone
	btfss sidetone						; is sidetone on right now ?
	goto wavesidetone_off				; no, go turn the wavesidetone line off
	incf wavesidetone_counter, F
	movfw wavesidetone_counter_setting
	subwf wavesidetone_counter, W
	btfss STATUS, Z
	goto wavesidetone_not_equal
	btfss wavesidetone					; see what state we are in and toggle it
	goto on_and_reset_wavesidetone_ctr	; wavesidetone is off right now, jump up and set it
	nop
	bcf wavesidetone					; wavesidetone is on right now, turn it off
	goto reset_wavesidetone_counter		; jump ahead and reset counter
on_and_reset_wavesidetone_ctr
	bsf wavesidetone					; turn on wavesidetone line
reset_wavesidetone_counter
	clrf wavesidetone_counter
	goto end_of_wavesidetone			;-------------------------------------------
wavesidetone_off
	bcf wavesidetone
	nop									; time padding with nops
	nop									; this evens things out so each run through this
	nop									;    soubroutine should be the same amount of time
	nop									;    regardless of the state or branches
	nop
wavesidetone_not_equal
	nop
	nop
	nop
	nop
	nop
	nop
	nop
end_of_wavesidetone

	return

; ------------------

loop_cw_unit

	; this subroutine loops for one CW unit (i.e. a dit)
	; if the sidetone line has been set high it will produce a square wave sidetone signal on the wavesidetone line
	; during the looping, the dit and dah paddles are checked and dit_buffer and dah_buffer set if either are pressed
	; this enables dit and dah insertion for iambic operation

	; initialize counters with calculated values
	movfw cw_unit_counter+0
	movwf counter1	;MSB
	movfw cw_unit_counter+1
	movwf counter2	;LSB

	ifdef include_paddle_reverse_code
	btfsc paddle_reverse				; are we in reverse paddle mode?
	goto loop_cw_unit_reverse_paddle	; yes, go to subroutine for reverse paddle mode
	endif

loop_de_loop_init
	movlw d'3'
	movwf counter3

loop_de_loop

	btfss sending_dit				; skip next line if we are sending dit
	goto check_for_dit				; if we are sending dit, check dah paddle
	btfss dah						; check the dah paddle, if = 1, skip over next line
	bsf dah_buffer					; dah is keyed, set buffer
	goto skip_over_check_for_dit	; skip over check_for_dit

check_for_dit						; we're sending a dah, check the dit paddle
	btfss dit						; if dit is not keyed, skip over next line
	bsf dit_buffer					; set the dit buffer
	nop
skip_over_check_for_dit

	call produce_wavesidetone

	decfsz counter3,F
	goto loop_de_loop
	decfsz counter2, F
	goto loop_de_loop_init
	decfsz counter1, F
	goto loop_de_loop_init

	return

	ifdef include_paddle_reverse_code

loop_cw_unit_reverse_paddle

loop_de_loop_init_rev
	movlw d'3'
	movwf counter3
loop_de_loop_rev
	btfss sending_dit		; skip next line if we are sending dit
	goto check_for_dit_rev		; if we are sending dit, check dah paddle
	btfss dit				; check the dit paddle, if = 1, skip over next line
	bsf dah_buffer			; dah is keyed, set buffer
	goto skip_over_check_for_dit_rev			; skip over check_for_dit

check_for_dit_rev				; we're sending a dah, check the dit paddle
	btfss dah				; if dah is not keyed, skip over next line
	bsf dit_buffer			; set the dit buffer
	nop
skip_over_check_for_dit_rev

	call produce_wavesidetone

	decfsz counter3,F
	goto loop_de_loop_rev
	decfsz counter2, F
	goto loop_de_loop_init_rev
	decfsz counter1, F
	goto loop_de_loop_init_rev

	return

	endif ;include_reverse_paddle_code

; ------------------

loop_6_cw_units
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	return

; ------------------

send_dit_or_dah

	; send a dit or dah
	; parms:
	;
	;	sending_dit
	;		1 = send dit
	;		0 = send dah
	;	key_tx_active
	;		1 = key tx line
	;		0 = do not key tx line
	;
	; notes:
	;
	;	- sidetone line is always keyed (unless sidetone_off_during_tx is true and key_tx_active is true)

	ifdef include_toggle_sidetone_on_off_code
	btfsc sidetone_off_during_tx
	btfss key_tx_active
	endif
	bsf sidetone		; turn on sidetone
	btfsc key_tx_active	; if key_tx is clear, jump - don't key tx line
	bsf key				; key tx line
	btfss sending_dit	; are we sending a dit ?
	goto send_a_dah		; if not, jump to send_a_dah
	call loop_cw_unit	; loop for one unit, a dit
	goto clear_lines	; get out and clear all the lines

send_a_dah

	ifdef include_weighting_code
	movfw cw_unit_counter+1			; store the regular counter values
	movwf cw_unit_counter_dit+1
	movfw cw_unit_counter+0
	movwf cw_unit_counter_dit+0
	movfw cw_unit_counter_dah+1		; switch in the dah values for weighting
	movwf cw_unit_counter+1
	movfw cw_unit_counter_dah+0
	movwf cw_unit_counter+0
	endif ;include_weighting_code

	call loop_cw_unit	; key for three lengths (dah)
	call loop_cw_unit
	call loop_cw_unit

	ifdef include_weighting_code
	movfw cw_unit_counter_dit+1		; put regular counter values back
	movwf cw_unit_counter+1
	movfw cw_unit_counter_dit+0
	movwf cw_unit_counter+0
	endif ;include_weighting_code

clear_lines
	bcf sidetone	; deactivate sidetone
	bcf key			; deactivate key line
	call loop_cw_unit	; loop while unkeyed for inter-cw_unit spacing

	ifdef include_nnnnn_code			; this code blows, so I keep it turned off.
										; only use if you're too cheap to buy a mode switch
	; keep a history of dits and dahs
	;   history is stored in a word consisting of ditdah_history1 and 2
	;   each bit is one cw_unit : 0 = dit, 1 = dah
	;                                     m             l     m             l
	;                                     s             s     s             s
	;                                     b             b     b             b
	;   cw_units are pushed like this --&gt; ditdah_history1 --&gt; ditdah_history2
	btfss key_tx_active		; are we actually keying the tx ?
	return					; if not, blow out and do not add to history
	bsf STATUS, C			; set the carry bit, 1 = dah
	btfsc sending_dit		; did we just send a dit ?
	bcf STATUS, C			; if so, clear the carry bit, 0 = dit
	rrf ditdah_history1, F	; place bit into history register
	rrf ditdah_history2, F	; carry over into the second byte
	movlw 0xff
	movwf ditdah_history_timer1
	endif ;include_nnnnn_code

	return

; ------------------

	ifdef include_freq_counter_code

store_freq_offset_in_eeprom

	errorlevel -302
	movlw eeprom_freq_offset_high
	banksel EEADR
	movwf EEADR
	banksel freq_offset_binary
	movfw freq_offset_binary+0
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	movlw eeprom_freq_offset_low
	banksel EEADR
	movwf EEADR
	banksel freq_offset_binary
	movfw freq_offset_binary+1
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	return

	errorlevel +302

	endif ;include_freq_counter_code

; ------------------

initialize_eeprom_first_time

	errorlevel -302
	movlw eeprom_addr_speed_wpm		; address 0x00 last wpm speed
	banksel EEADR
	movwf EEADR
	movlw initial_speed_wpm
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	; write wavesidetone freq to eeprom
	movlw eeprom_wavesidetone_setting
	banksel EEADR
	movwf EEADR
	movlw _initial_wavesidetone_setting
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	;clean out memory locations
	movlw eeprom_memory_location_0
	banksel EEADR
	movwf EEADR
	movlw 0xff
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom
	movlw eeprom_memory_location_1
	banksel EEADR
	movwf EEADR
	banksel PORTA
	call write_eeprom
	movlw eeprom_memory_location_2
	banksel EEADR
	movwf EEADR
	banksel PORTA
	call write_eeprom
	errorlevel +302

	ifdef include_freq_counter_code

	errorlevel -302
	movlw eeprom_freq_calib_high
	banksel EEADR
	movwf EEADR
	movlw high _frequency_counter_calib
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	movlw eeprom_freq_calib_low
	banksel EEADR
	movwf EEADR
	movlw low _frequency_counter_calib
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom

	call store_freq_offset_in_eeprom

	errorlevel +302

	endif ;include_freq_counter_code

	; clear eeprom_settings1 location
	clrf eeprom_settings1
	call write_eeprom_settings1

	ifndef exclude_delays_debug
	ifdef include_funky_beeps_code
	call beep_boop
	call beep_boop
	call beep_boop
	endif ;include_funky_beeps_code
	endif ;exclude_delays_debug

	banksel PORTA

	return

; ------------------

write_eeprom_settings1

	errorlevel -302
	movlw eeprom_settings1_location
	banksel EEADR
	movwf EEADR
	banksel eeprom_settings1
	movfw eeprom_settings1
	banksel EEDATA
	movwf EEDATA
	banksel PORTA
	call write_eeprom
	errorlevel +302

	banksel PORTA

	return

; ------------------

initialize

	ifndef exclude_delays_debug
	movlw d'2'			; pause to make sure everything is settled down on power up
	call pause_w_100ms
	endif ;exclude_delays_debug

	;bcf OPTION_REG,NOT_RBPU

	errorlevel -302
	ifndef processor_low_end
	banksel CMCON
	movlw 0x07			; setup Port A for I/O, not comparator mode
	movwf CMCON
	endif ;processor_low_end
	errorlevel +302

	; initialize IO Ports
	errorlevel -302
	banksel TRISA
	movlw B'00010111'
	movwf TRISA
	movlw B'00001101'	; 1 = input, 0 = output
	movwf TRISB
	errorlevel -302
	banksel PORTA
	bcf dit_buffer
	bcf dah_buffer
	bcf key
	bcf sidetone
	ifdef include_txwake_line_code
	bcf txwake
	endif
	bsf key_tx_active

	ifdef include_nnnnn_code
	clrf ditdah_history1
	clrf ditdah_history2
	endif

	; see if eeprom has been written to before
	movlw 0x00
	errorlevel -302
	banksel EEADR
	movwf EEADR
	banksel EECON1
	bsf EECON1, RD		; read address 0x00
	banksel EEDATA
	btfsc EEDATA,0x07					; is bit 7 set high ?
	call initialize_eeprom_first_time	; if so, we got a new chip here
	banksel PORTA
	errorlevel +302

	; read the last wpm stored in eeprom
	movlw eeprom_addr_speed_wpm
	call read_eeprom
	movwf speed_wpm
	ifdef include_weighting_code
	movwf speed_wpm_dah
	endif

	call calculate_cw_unit_values

	; setup eeprom memory locations
	movlw eeprom_memory_location_0
	movwf eeprom_memory_locations+0
	movlw eeprom_memory_location_1
	movwf eeprom_memory_locations+1
	movlw eeprom_memory_location_2
	movwf eeprom_memory_locations+2

	movlw eeprom_memory_loc_0_cw_units
	movwf eeprom_memory_loc_limits+0
	movlw eeprom_memory_loc_1_cw_units
	movwf eeprom_memory_loc_limits+1
	movlw eeprom_memory_loc_2_cw_units
	movwf eeprom_memory_loc_limits+2

	;read eeprom_settings1 from eeprom
	movlw eeprom_settings1_location
	call read_eeprom
	movwf eeprom_settings1

	movlw eeprom_wavesidetone_setting
	call read_eeprom
	movwf wavesidetone_counter_setting
	clrf wavesidetone_counter

	ifdef include_interrupt_code
	; set up interrupt stuff
	errorlevel -302
	banksel OPTION_REG
	bcf OPTION_REG, T0CS	; TIMER0 clock source set to internal clock (clock / 4)
	bcf OPTION_REG, PSA		; assign prescaler to TIMER0 clock
	bsf OPTION_REG, PS0		; set TIMER0 prescaler to 111 ( / 256 )
	bsf OPTION_REG, PS1
	bsf OPTION_REG, PS2
	banksel TMR0
	movlw d'150'
	movwf TMR0
	banksel INTCON
	bsf INTCON, T0IE		; enable TIMER0 overflow interrupt
	bsf INTCON, GIE			; enable interrupts
	errorlevel -302
	banksel PORTA
	endif

	ifdef include_cw_rx_practice_code
	movlw 0x30				; seed random number generator
	movwf randomnum+0
	movlw 0x45
	movwf randomnum+1
	endif

	ifdef include_freq_counter_code
	movlw eeprom_freq_calib_high
	call read_eeprom
	movwf frequency_counter_calib+0
	movlw eeprom_freq_calib_low
	call read_eeprom
	movwf frequency_counter_calib+1

	; read freq_offset from EEPROM
	movlw eeprom_freq_offset_high
	call read_eeprom
	movwf freq_offset_binary+0
	movlw eeprom_freq_offset_low
	call read_eeprom
	movwf freq_offset_binary+1

	endif ;include_freq_counter_code

	ifdef include_serial_port_rx_code
	errorlevel -302
	banksel TRISB
	bsf TRISB,0x01		; make RB1 an input
	banksel TXSTA
;    bsf TXSTA,BRGH		; high speed bit generator
    bcf TXSTA,BRGH		; low speed bit generator
	movlw d'25'			; 9600 baud with 4Mhz clock / 2400 with low speed bit generator
	banksel SPBRG
	movwf SPBRG
	banksel TXSTA
	bcf TXSTA,SYNC		; asyncronous mode
	;bcf STATUS,RP0		; select bank 0
	banksel RCSTA
	bsf RCSTA,SPEN
	bsf RCSTA,CREN		; enable serial receive
read_rx_fifo_loop
	call pause_100_ms
	banksel PIR1
	btfss PIR1,RCIF		; do we have something in the buffer?
	goto read_rx_fifo_loop_exit				; no, blow out of here
	banksel RCREG
	movfw RCREG			; pull byte out of the serial rx FIFO
	goto read_rx_fifo_loop
read_rx_fifo_loop_exit
	errorlevel +302
	endif ;include_serial_port_code

	banksel PORTA

	bcf send_cw_preemptible
	bcf squeeze_detected
	bcf in_beacon_mode

	ifdef include_aux_line_code
	bcf aux				;set aux line low initially
	endif ;include_aux_line_code

	retlw	0

; ------------------

; ------------------

	ifdef include_serial_cw_keyboard

check_serial_cw_keyboard
	btfsc PIR1,OERR		; did we overrun the buffer?
	goto handle_overrun
	btfsc PIR1,FERR		; did we have a frame error?
	goto handle_frame_error
	btfss PIR1,RCIF		; do we have something in the buffer?
	return				; no, blow out of here
	movfw RCREG			; pull byte out of the serial rx FIFO

	ifdef include_serial_rx_debug_code
	; debug code - send received byte in decimal CW
	movwf binary2bcd_binary_in+1
	clrf binary2bcd_binary_in+0
	call binary2bcd
	call rotate_BCD012_left
	call rotate_BCD012_left
	call send_BCD2_in_cw
	call rotate_BCD012_left
	call rotate_BCD012_left
	call send_BCD2_in_cw
	call rotate_BCD012_left
	call rotate_BCD012_left
	call send_BCD2_in_cw
	return
	else

	sublw d'49'					; convert ascii code to cw table lookup 0-9
	movwf temp_memory			; double it
	addwf temp_memory, F
	movfw temp_memory
	call cw_table				; get the first byte of code
	movwf cw_char_to_send+0
	movfw temp_memory
	addlw 0x01					; increment it to get the next byte
	call cw_table
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	return

	endif ;include_serial_rx_debug_code

handle_overrun
	movfw RCREG
	ifdef include_debug_code
	call low_beep
	endif
	return

handle_frame_error
	bcf RCSTA,CREN
	bsf RCSTA,CREN
	ifdef include_debug_code
	call low_beep
	endif
	return

	endif ;include_serial_port_rx_code

; ------------------

check_straight_key_mode

	; this checks if the dah paddle is keyed or mode button is pressed, if so, we loop in straight key mode forever
	btfss mode_switch
	goto go_into_straight_key_mode
	btfsc dah  	; if dah = 0 go to straight_key_loop
	retlw 0		;   otherwise, return
go_into_straight_key_mode
	bsf sending_dit
	movlw d'60'
	movwf speed_wpm
	call calculate_cw_unit_values

	ifdef include_txwake_line_code
	bcf txwake
	endif ;include_txwake_line_code

straight_key_loop
	call loop_cw_unit
	btfss mode_switch
	goto key_on
	btfsc dit   		; if dit = 0 jump to key_on
	goto key_off		; else go to key_off
key_on
	ifdef include_txwake_line_code
	;call reset_txwake_timer
	movlw HIGH _txwake_delay_time_straight_key
	movwf txwakecounter+0
	movlw LOW _txwake_delay_time_straight_key
	movwf txwakecounter+1
	bsf txwake
	endif
	bsf key				; key the TX
	ifdef include_toggle_sidetone_on_off_code
	btfss sidetone_off_during_tx
	endif ;include_toggle_sidetone_on_off_code
	bsf sidetone		; key the sidetone
	goto straight_key_loop
key_off
	ifdef include_txwake_line_code
	bcf dit_buffer
	bcf dah_buffer
	pagesel check_txwake_clear_time
	errorlevel -306
	call check_txwake_clear_time
	errorlevel +306
	endif
	bcf key			; unkey the TX
	bcf sidetone	; unkey the sidetone
	goto straight_key_loop

	retlw 0

; ------------------

send_buffer

	btfss dit_buffer	; if dit_buffer is set, skip next line
	goto check_dah_buffer
	ifdef include_txwake_line_code
	bsf txwake
	endif
	bcf dit_buffer
	bsf sending_dit
	call send_dit_or_dah

	ifdef include_iambic_mode_code
    btfsc iambic_b_mode
    goto check_dah_buffer
	btfss squeeze_detected
    goto check_dah_buffer
	btfss dit
	goto check_dah_buffer
	btfss dah
	goto check_dah_buffer
	bcf squeeze_detected
	bcf dit_buffer
	bcf dah_buffer
	endif ;include_iambic_mode_code

check_dah_buffer
	btfss dah_buffer	; if dah_buffer is set, skip next line
	return
	ifdef include_txwake_line_code
	bsf txwake
	endif
	bcf dah_buffer
	bcf sending_dit

	ifdef include_bug_code		; HACK ALERT: special hack to accomodate bug mode
	btfss bug_mode_on
	goto no_bug_mode
	movfw speed_wpm				; temporarily store the current wpm
	movwf temp_memory
	movlw d'60'					; jack up the wpm to 60 so loop is responsive
	movwf speed_wpm
	call calculate_cw_unit_values
	bsf sending_dit
bug_loop
	ifdef include_toggle_sidetone_on_off_code
	btfss sidetone_off_during_tx
	endif ;include_toggle_sidetone_on_off_code
	bsf sidetone
	bsf key
	call loop_cw_unit
	btfss dah
	goto bug_loop				; loop as long as dah is on
	bcf sidetone
	bcf key
	movfw temp_memory			; restore original wpm
	movwf speed_wpm
	call calculate_cw_unit_values
	bcf dah_buffer
	return
no_bug_mode
	endif ;include_bug_code

	call send_dit_or_dah

	ifdef include_iambic_mode_code
    btfsc iambic_b_mode
    return
	btfss squeeze_detected
    return
	btfss dit
	return
	btfss dah
	return
	bcf squeeze_detected
	bcf dit_buffer
	bcf dah_buffer
	endif ;include_iambic_mode_code

	return
; ------------------

decrement_wpm

	movfw speed_wpm
	sublw d'4'			; 5 wpm lower limit
	btfsc STATUS, C		;if 4 - speed_wpm &gt; 0, return and do not decrement
	return
	decf speed_wpm, F
	ifdef include_weighting_code
	decf speed_wpm_dah, F
	endif
	call calculate_cw_unit_values
	return

; ------------------

increment_wpm

	movfw speed_wpm
	sublw maxcwspeed	; max wpm upper limit
	btfss STATUS, C		;if maxcwspeed - speed_wpm &lt; 0, return and do not increment
	return
	incf speed_wpm, F
	ifdef include_weighting_code
	incf speed_wpm_dah, F
	endif
	call calculate_cw_unit_values
	return

; ------------------

send_cw

	; sends two bytes of CW
	; parms:
	;	cw_char_to_send:2

	; 00 = terminator = end of character or string, return
	; 01 = dit
	; 11 = dah
	; 10 = intercharacter spacing - pause for two more cw_units lengths and go on

	; cw_char_to_send+0 is sent first, cw_char_to_send+1 is sent second; MSB is the first sent

	movlw cw_char_to_send-1	; setup pointer, set to first byte - 1
	movwf FSR
	movlw 0x02				; loop through twice
	movwf count

loop_initialize
	incf FSR, F
	movlw b'11000000'		; initialize send_cw_temp
	movwf send_cw_temp

cw_byte_decode

	btfss send_cw_preemptible
	goto no_send_cw_preemptible
	call check_for_button_hit		; see if a button was hit for us to exit out
	sublw 0x01
	btfsc STATUS,Z
	goto send_cw_button_hit_loop
no_send_cw_preemptible
	movfw send_cw_temp
	movwf temp				;make a copy of send_cw_temp
	movfw INDF
	andwf send_cw_temp, W
	btfsc STATUS, Z			; if 0, we have a terminator, exit
	goto loop_2_cw_units_and_exit
	subwf send_cw_temp, W
	btfsc STATUS, Z ; if 0, we have a dah
	goto send_cw_dah_byte
	; check for 10
	movlw b'10101010'	; create a bit mask
	andwf temp, F		; and it with the temp register so we get 10 in the right position
	movfw INDF		; get a fresh byte of cw again
	subwf temp, W		; subtract
	btfsc STATUS, Z		; if we have zero, this is 10 - an intercharacter space
	goto loop_two_cw_units ; zero set = 10
	bsf sending_dit		; zero not set = 01 - send a dit
	call send_dit_or_dah
	goto cw_byte_decode_next
send_cw_dah_byte
	bcf sending_dit
	call send_dit_or_dah
cw_byte_decode_next		; get things ready to read the next two bits to the right
	bcf STATUS, C		; rotate bits to the right twice
	rrf send_cw_temp, F		; to select the next two bits
	rrf send_cw_temp, F
	btfss STATUS, C		; if a bit gets into carry flag, we're at zero and we're done
	goto cw_byte_decode	; otherwise, go back for more with this byte
	decf count, F		; we're done with this byte, decrement the count
	btfss STATUS, Z		; did count hit zero ?
	goto loop_initialize

loop_2_cw_units_and_exit
	; loop two cw_units lengths so we have proper inter character spacing
	call loop_cw_unit
	call loop_cw_unit

	return

loop_two_cw_units
	call loop_cw_unit
	call loop_cw_unit
	goto cw_byte_decode_next

send_cw_button_hit_loop
	call loop_check_for_button_hit
	ifdef include_function_button_code
	bsf memory_playback_manual_exit
	endif
	return

;-------------

send_BCD2_in_cw

	swapf BCD2,F			; send upper nibble
	movfw BCD2
	andlw b'00001111'
	call send_w_BCD_in_cw
	swapf BCD2,F			; send lower nibble
	movfw BCD2
	andlw b'00001111'
	call send_w_BCD_in_cw

	return

;-------------

announce_speed_wpm

	; convert speed_wpm to BCD
	movlw 0x00
	movwf binary2bcd_binary_in+0
	movfw speed_wpm
	movwf binary2bcd_binary_in+1
	call binary2bcd

	call send_BCD2_in_cw

	return

;-------------

speed_mode

	; this mode allows you to change the sending speed
	; dit paddle speeds up
	; dah paddle slows down
	; mode button or paddle squeeze exits

	bsf sending_dit

	movlw 0x04
	call pause_w_100ms

dit_loop
	call send_dit_or_dah	; sound a dit

	btfss dit				; dit is pressed, dah is not
	btfss dah
	goto skip_increment_wpm
	call increment_wpm
	goto dit_loop
skip_increment_wpm
	btfss dah				; dah is pressed, dit is not
	btfss dit
	goto skip_decrement_wpm
	call decrement_wpm
	goto dit_loop
skip_decrement_wpm

	btfss mode_switch		; if switch is pressed, get out of loop
	goto loop_while_pressed2
	btfss dit				; if both paddles are pressed, this also exits
	btfsc dah
	goto dit_loop

loop_while_pressed2			; wait for mode_switch, dit and dah to be released
	call pause_100_ms
	btfss mode_switch
	goto loop_while_pressed2
	btfss dit
	goto loop_while_pressed2
	btfss dah
	goto loop_while_pressed2

	movlw 0x05
	call pause_w_100ms

	call write_speed_wpm_to_eeprom

	movlw 0x05
	call pause_w_100ms

	bcf dit_buffer
	bcf dah_buffer

	return

; ------------------

write_speed_wpm_to_eeprom

	movlw eeprom_addr_speed_wpm
	errorlevel -302
	banksel EEADR
	movwf EEADR
	banksel speed_wpm
	movfw speed_wpm
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA

	call write_eeprom

	return

; ------------------

	ifdef include_tune_mode_code

tune_mode

	; this mode is used for tuning up a rig
	; left paddle intermittently turns on TX
	; right paddle latches TX
	; mode switch or paddle squeeze exits

	bcf tune_latch_mode
	movlw 0x02
	call pause_w_100ms
	bsf sending_dit
	call send_dit_or_dah
	movfw speed_wpm		; temporarily store the current wpm
	movwf temp_memory
	movlw d'60'			; jack up the wpm to 60 so loop is responsive
	movwf speed_wpm
	call calculate_cw_unit_values

tune_mode_loop

	call loop_cw_unit
	btfss dit				; is dit pressed ?
	goto tune_dit_pressed	; if so, go to tune_dit_pressed
	btfss dah				; is dah pressed ?
	goto tune_dah_pressed	; if so, go to tune_dah_pressed
	btfsc tune_latch_mode	; are we in latch mode ?
	goto tune_mode_check_for_exit	; if so jump over
	bcf key					; we're not in latch mode and dit is not pressed
	bcf sidetone			;  so clear key and sidetone
	ifdef include_txwake_line_code
	bcf txwake
	endif

tune_mode_check_for_exit
	btfss mode_switch		; if switch is pressed, get out of loop
	goto tune_mode_loop_while_pressed
	btfss dit				; if both paddles are pressed, this also exits
	btfsc dah
	goto tune_mode_loop

tune_mode_loop_while_pressed			; wait for mode_switch, dit and dah to be released

	bcf key
	bcf sidetone
	ifdef include_txwake_line_code
	bcf txwake
	endif

	call pause_100_ms
	btfss mode_switch
	goto tune_mode_loop_while_pressed
	btfss dit
	goto tune_mode_loop_while_pressed
	btfss dah
	goto tune_mode_loop_while_pressed

	movfw temp_memory			; restore original wpm
	movwf speed_wpm
	call calculate_cw_unit_values

	movlw 0x02					; exiting dit
	call pause_w_100ms
	bsf sending_dit
	call send_dit_or_dah

	bcf dit_buffer				; clean out buffers in case they got set in loop_cw_unit
	bcf dah_buffer

	return

tune_dit_pressed
	bcf tune_latch_mode		; get out of latch mode
	ifdef include_txwake_line_code
	bsf txwake
	endif
	bsf key					; key tx
	ifdef include_toggle_sidetone_on_off_code
	btfss sidetone_off_during_tx
	endif ;include_toggle_sidetone_on_off_code
	bsf sidetone			; key sidetone
	call loop_cw_unit		;
	goto tune_mode_check_for_exit

tune_dah_pressed
	bsf tune_latch_mode		; go into latch mode
	btfss key
	goto tune_key
	bcf key
	bcf sidetone
	call pause_100_ms
	ifdef include_txwake_line_code
	bcf txwake
	endif
	goto tune_dah_pressed_loop
tune_key
	ifdef include_txwake_line_code
	bsf txwake
	endif
	bsf key
	ifdef include_toggle_sidetone_on_off_code
	btfss sidetone_off_during_tx
	endif ;include_toggle_sidetone_on_off_code
	bsf sidetone
	call loop_cw_unit		;

tune_dah_pressed_loop		; wait for dah to be released
	call loop_cw_unit
	btfss dit						; if dit is pressed,
	goto tune_mode_check_for_exit	;  blow out of the loop because both dit and dah are squeezed
	btfss dah				; if dah is not pressed, skip over
	goto tune_dah_pressed_loop

	goto tune_mode_check_for_exit

	endif ;include_tune_mode_code

; ------------------

	ifdef include_calibration_code

calibration_check_mode

	; this mode allows you to check the cw unit timing calibration
	; hold down the dit paddle for a minute or half minute
	; after releasing the paddle, the number of dits will be announced in CW
	; use the formula below to determine the proper number of dits for the wpm setting
	; speed in wpm = dits per min / 25
	; 20 WPM equals 500 dits per minute or 250 dits per 30 seconds

	clrf binary2bcd_binary_in+0
	clrf binary2bcd_binary_in+1
	bsf sending_dit

calibration_check_mode_wait_loop
	btfsc dit							; loop while waiting for dit to be pressed
	goto calibration_check_mode_wait_loop
calibration_dit_loop
	call send_dit_or_dah				; send a dit
	incf binary2bcd_binary_in+1, F		; increment the dit counter
	btfsc STATUS, Z						; did the lower byte roll over ?
	incf binary2bcd_binary_in+0, F		; if so, increment the upper byte
	btfss dit							; if dit is still pressed
	goto calibration_dit_loop			;   continue to loop

	movlw 0x05
	call pause_w_100ms

	call binary2bcd

	; break out lower nibble of middle BCD digit
	movfw BCD1
	andlw b'00001111'
	movwf bcd_low_nibble
	addwf bcd_low_nibble, F 	; double it so we're in the right spot in cw_table

	movfw bcd_low_nibble		; send the low nibble BCD digit in CW
	call cw_table
	movwf cw_char_to_send+0
	movfw bcd_low_nibble
	addlw 0x01
	call cw_table
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306

	; send least signficant BCD digit
	call send_BCD2_in_cw

	return

	endif
; ------------------

program_memory

	; parms
	;	memory_number (0 = callsign memory)
	;
	; calls
	;	send_dit_or_dah
	;	check_paddles
	;	pause_w_100ms
	;	divide16bit

	; send a dit or boop_beep to signify start
	movlw 0x02
	call pause_w_100ms
	ifdef include_funky_beeps_code
	call boop_beep
	else
	bsf sending_dit
	call send_dit_or_dah
	endif

	; initialize counters
	clrf cw_unit_count

	bcf temp_memory_dirty

	movlw b'11000000'
	movwf temp_memory_mask
	clrf temp_memory

	; get parameters for this memory
	movlw eeprom_memory_locations			; get the *address* of memory location array
	movwf FSR
	movfw memory_number						; add for the appropriate memory location
	addwf FSR, F
	movfw INDF
	movwf temp_memory_eeprom_pointer		; initialize the live eeprom pointer
	movwf start_of_memory_location			; initialize the static starting location
	incf temp_memory_eeprom_pointer, F		; actual recording starts at +1

initial_recording_loop
	; check if mode button was hit
	pagesel we_are_done_with_programming
	errorlevel -306
	btfss mode_switch
	goto we_are_done_with_programming
	errorlevel +306
	pagesel check_paddles
	errorlevel -306
	call check_paddles
	errorlevel +306
	btfsc dit_buffer
	goto add_dit_or_dah_to_memory
	btfsc dah_buffer
	goto add_dit_or_dah_to_memory
	goto initial_recording_loop

init_recording_loop_counters
	movlw 0x00
	movwf recording_loop_counter1
	movwf recording_loop_counter2

recording_loop
	ifdef include_function_button_code
	btfss switch0
	goto we_are_done_with_programming
	btfss switch1
	goto we_are_done_with_programming
	btfss switch2
	goto we_are_done_with_programming
	endif
	pagesel we_are_done_with_programming
	errorlevel -306
	btfss mode_switch
	goto we_are_done_with_programming
	errorlevel +306
	pagesel check_paddles
	errorlevel -306
	call check_paddles
	errorlevel +306
	btfsc dit_buffer
	goto store_cw_unit_in_temp_memory
	btfsc dah_buffer
	goto store_cw_unit_in_temp_memory
	; nothing is active right now, increment the inactive counters
	incfsz recording_loop_counter1, F
	goto recording_loop
	movfw recording_loop_counter2
	sublw 0xFF
	btfss STATUS, Z
	incf recording_loop_counter2, F
	goto recording_loop

we_are_done_with_programming
	; store the last byte if temp_memory is dirty
	btfsc temp_memory_dirty
	call store_temp_memory_to_eeprom
	movfw cw_unit_count				;see if cw_unit_count is zero
	btfsc STATUS,Z
	decf cw_unit_count,F			;if so, store FF in cw_unit_count to signify blank memory
	; store the cw_unit_count in the first byte
	movfw start_of_memory_location
	errorlevel -302
	banksel EEADR
	movwf EEADR
	errorlevel +302
	banksel PORTA
	movfw cw_unit_count
	errorlevel -302
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA
	call write_eeprom
	movlw 0x03
	call pause_w_100ms
	ifdef include_funky_beeps_code
	call beep_boop
	else
	bsf sending_dit
	call send_dit_or_dah
	endif
	return ; we are totally done

    ;------------- sub-subroutines

store_cw_unit_in_temp_memory

	; store the silent timee before the cw_unit
	; first, calculate the ratio between the silent and the cw unit time
	movfw cw_unit_counter+1
	movwf divisor+1
	movfw cw_unit_counter+0
	movwf divisor+0
	movfw recording_loop_counter1	; recording_loop_counter / cw_unit_counter = space ratio
	movwf dividend+1				; this is used below to determine if we have an intercharacter space
	movfw recording_loop_counter2	; or an interword space
	movwf dividend+0
	call divide16bit
	movfw quotient+0				; check if quotient MSB is zero
	btfsc STATUS, Z
	goto measure_up_silent_time		; if so, skip over this stuff
	movlw 0xff						; if not, set LSB to FF
	movwf quotient

measure_up_silent_time
	movfw quotient+1	; see how many cw_unit lengths we had silent before this cw_unit
	sublw d'1'							; is this an intercharacter space ? was 10
	btfsc STATUS, C
	goto add_dit_or_dah_to_memory		; no, it's negative, don't add a space cw_unit
	movfw quotient+1					; is this an interword space ?
	sublw d'14'							; was 70
	btfsc STATUS, C
	goto skip_over_1					; result is negative, we have less than 6 cw_units
	movlw b'00000000'					; 00 = 6 cw_unit space
	goto skip_over_2
skip_over_1
	movlw b'10101010'					; 10 = 2 cw_unit space
skip_over_2
	call store_w_in_temp_memory

add_dit_or_dah_to_memory
	btfss dit_buffer
	goto skip_over_dit
	bsf sending_dit
	call send_dit_or_dah
	movlw b'01010101'	; 01 = dit
	bcf dit_buffer		; clean out dit buffer
	goto skip_over_dah
skip_over_dit
	bcf sending_dit
	call send_dit_or_dah
	movlw b'11111111'	; 11 = dah
	bcf dah_buffer		; clean out dah buffer
skip_over_dah
	call store_w_in_temp_memory

	; enforce cw unit limit count so we don't run over into another memory
	; get cw_unit count limit
	movlw eeprom_memory_loc_limits+0
	movwf FSR
	movfw memory_number						; add for the appropriate memory location
	addwf FSR, F
	movfw INDF
	subwf cw_unit_count, W					; does cw_unit_count = cw_unit count limit ?
	btfss STATUS, Z
	goto init_recording_loop_counters 		; no, go back for more
	bsf sidetone							; yes, lock up the sidetone
wait_for_user_to_realize
	ifdef include_funky_beeps_code
	call low_beep
	endif
	call pause_100_ms
	btfss dit
	goto wait_for_user_to_realize
	btfss dah
	goto wait_for_user_to_realize
	bcf sidetone
	bcf dit_buffer
	bcf dah_buffer
	goto we_are_done_with_programming

	;cleanup

store_w_in_temp_memory
	andwf temp_memory_mask, W			; apply the mask to W
	addwf temp_memory, F				; add the cw_unit to temp_memory
	;increment cw_unit count
	incf cw_unit_count, F

;setup_for_next_store_w
	bsf temp_memory_dirty
	rrf temp_memory_mask, F				; move the bit mask over
	rrf temp_memory_mask, F
	btfsc STATUS, C						; if we carried, we need to start a new byte
	call store_temp_memory_to_eeprom
	return

store_temp_memory_to_eeprom
	movwf temp_w	; preserve w
	; write the temp memory byte to eeprom
	movfw temp_memory_eeprom_pointer
	errorlevel -302
	banksel EEADR
	movwf EEADR
	errorlevel +302
	banksel PORTA
	movfw temp_memory
	errorlevel -302
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA
	errorlevel -306
	pagesel check_paddles
	call check_paddles
	errorlevel +306
	call write_eeprom
	errorlevel -306
	pagesel check_paddles
	call check_paddles
	errorlevel +306
	;get things ready for new byte
	movlw b'11000000'
	movwf temp_memory_mask
	clrf temp_memory
	; point to the next eeprom byte to be written
	incf temp_memory_eeprom_pointer, F
	; TODO - check if we're at the limit for this memory
	movfw temp_w	; restore w
	bcf temp_memory_dirty
	return

;	endif

; ------------------

read_eeprom

	; read eeprom address in w
	; return data in w
	errorlevel -302
	banksel EEADR
	movwf EEADR
	banksel EECON1
	bsf EECON1, RD
	banksel EEDATA
	movfw EEDATA
	errorlevel +302
	banksel PORTA
	return

; ------------------

check_for_button_hit
	btfss dit
	retlw 1
	btfss dah
	retlw 1
	btfss mode_switch
	retlw 1
	ifdef include_function_button_code
	btfss switch0
	retlw 1
	btfss switch1
	retlw 1
	btfss switch2
	retlw 1
	endif
	retlw 0

; ------------------

loop_check_for_button_hit
	call check_for_button_hit
	sublw 0x01
	btfsc STATUS,Z
	goto loop_check_for_button_hit
	call pause_100_ms
	return

; ------------------

;UNDER CONSTRUCTION MARKER
;******************************************************************************************
;******************************************************************************************
;******************************************************************************************
;******************************************************************************************
;******************************************************************************************
;******************************************************************************************

	ifdef enhanced_memory_support

get_cw_memory_character

	; this subroutine returns one cw element from a memory location
	; input parameters:
	;	memory_number = the number of the CW memory to access (0,1,2)
	;	cw_element_number = the number of the character to retrive (0-253)
	; output:
	;	returns the CW element in W register
	;		00 (0x00) = 6 CW unit space
	;		10 (0x02) = 2 CW unit space
	;		11 (0x03) = dah
	;		01 (0x01) = dit
	;		0xFF = memory location is empty
	;		0x04 = invalid CW element location
	;
	; &quot;local&quot; variables
	;
	;
	; 	CW Memory Organization
	;
	;    8 7 6 5 4 3 2 1
	;   +-+-+-+-+-+-+-+-+
	;   |               |  &lt;-- first byte is number of CW elements in the memory (1 = 1 CW element, 2 = 2 CW elements, 0xFF = empty memory)
	;   +-+-+-+-+-+-+-+-+
    ;   | 1 | 2 | 3 | 4 |  &lt;-- second byte is first four CW elements (each element is two bits)
	;   +-+-+-+-+-+-+-+-+
	;   | 5 | 6 | 7 | 8 |  &lt;-- third byte is CW elements 5 through 8
	;   +-+-+-+-+-+-+-+-+
	;

	; get number of CW elements for this memory
	movlw eeprom_memory_locations			; get the *address* of memory location array (a &quot;pointer&quot;)
	movwf FSR
	movfw memory_number						; add the memory_number (0,1,2) to move the pointer to the right eeprom memory location in the array
	addwf FSR, F
	movfw INDF								; pull the *value* of the array register into W
	call read_eeprom						; get the first byte in the memory - this is the number of CW elements currently in the memory
	movwf cw_memory_element_count			; copy the element count in a temporary register

	; check if memory location is empty
	sublw 0xFF								; is the register FF meaning empty memory?
	btfsc STATUS, Z
	retlw 0xFF								; yes - return with 0xFF

	; check if an invalid CW element number was requested
	movfw cw_element_number
	subwf cw_memory_element_count, 0		; calculate cw_memory_element_count - cw_element_number
	btfss STATUS, C							; if Carry is set, the result is positive or zero
	retlw 0x04								; invalid CW element location requested, return with 0x04

	; calculate the element number in the byte: 0, 1, 2, 3
	decf cw_element_number, F
	movfw cw_element_number
	iorlw 0x03								; get the two least significant bits
	movwf memory_location_element_number	; 00 = 1st element in a location, 01 = 2nd, 10 = 3rd, 11 = fourth

	;calculate the memory location in eeprom
	bcf STATUS, C								; here we rotate the two least significant bits off; the remaining bits will be the byte location in memory
	rrf  cw_element_number, F
	bcf STATUS, C
	rrf  cw_element_number, F					; this is the memory location (first location = 0)
	incf cw_element_number, F					; add one to it so we skip the first memory byte which is the CW element count

	; get the memory byte from eeprom
	movlw eeprom_memory_locations			; get the *address* of memory location array (a &quot;pointer&quot;)
	movwf FSR
	movfw memory_number						; add the memory_number (0,1,2) to move the pointer to the right eeprom memory location in the array
	addwf FSR, F
	movfw INDF								; pull the *value* of the array register into W
	addwf cw_element_number, 0				;
	call read_eeprom						; go get the byte

	; get the CW element out of the byte and return it
	movwf cw_character						; place the byte we read from eeprom above in a temporary register
	incf memory_location_element_number, F	; increment this register to set it up for a loop

get_the_two_bits_loop
	movlw 0x04
	subwf memory_location_element_number, 0
	btfsc STATUS, Z
	goto we_are_at_the_right_two_bits
	rrf cw_character, F
	rrf cw_character, F
	incf memory_location_element_number, F
	goto get_the_two_bits_loop

we_are_at_the_right_two_bits
	movlw 0x03								; mask the two least significant bits only
	iorwf cw_character, 0
	return									; and return them in W

; ------------------

play_memory ; enhanced version

	; this subroutine plays one of the CW memories
	; parameters:
	; memory_number = number of memory to play (first one = 0)

	ifdef include_function_button_code
	bcf memory_playback_manual_exit			; clear flag to tell calling subroutines if user manaully exited
	endif

	; get parameters for this memory
	movlw eeprom_memory_locations			; get the *address* of memory location array
	movwf FSR
	movfw memory_number						; add for the appropriate memory location
	addwf FSR, F
	movfw INDF
	movwf temp_memory_eeprom_pointer		; initialize the live eeprom pointer
	; read the cw_unit count for this memory
	call read_eeprom
	movwf cw_unit_count

	; check for blank memory
	movlw 0xFF
	subwf cw_unit_count, W	; is the cw_unit count 0xFF ?
	btfss STATUS, Z
	goto byte_loop			; no, skip over this
	ifdef include_funky_beeps_code
	call low_beep
	else
	bsf sending_dit			; yes, send three dits and blow out
	bcf key_tx_active
	call send_dit_or_dah
	call send_dit_or_dah
	call send_dit_or_dah
	bsf key_tx_active
	endif ;include_funky_beeps_code
	return

byte_loop
	; get a byte from eeprom
	incf temp_memory_eeprom_pointer, F	; move to the next byte
	movfw temp_memory_eeprom_pointer
	call read_eeprom
	movwf temp_memory
	movlw 0x04				; four cw_units in a byte
	movwf temp_memory_mask	; use temp_memory_mask as a cw_unit within a byte counter
cw_unit_loop
	; TODO - exit if a switch or paddle is hit

	rlf temp_memory, F
	btfsc STATUS, C
	goto first_bit_1
;first_bit_0
	rlf temp_memory, F
	btfsc STATUS, C
	goto cw_unit_01
;cw_unit_00	- 6 cw_unit space
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	goto next_cw_unit_setup
first_bit_1
	rlf temp_memory, F
	btfsc STATUS,C
	goto cw_unit_11
;cw_unit_10 - 2 cw_unit space
	call loop_cw_unit
	call loop_cw_unit
	goto next_cw_unit_setup
cw_unit_11 ; dah
	bcf sending_dit
	call send_dit_or_dah
	goto next_cw_unit_setup
cw_unit_01
	bsf sending_dit
	call send_dit_or_dah
next_cw_unit_setup
	decf cw_unit_count, F
	btfsc STATUS, Z				; did we do the last cw_unit ?
	return						; yes, blow out
    btfsc in_beacon_mode
	goto skip_button_check
	call check_for_button_hit
	sublw 0x01
	btfsc STATUS,Z
	goto button_hit_loop
skip_button_check
	decf temp_memory_mask, F	; no
	btfsc STATUS, Z				; did we do the last cw_unit in this byte ?
	goto byte_loop				; yes, grab another byte
	goto cw_unit_loop			; no, grab another cw_unit in this byte
	return

button_hit_loop
	call loop_check_for_button_hit
	ifdef include_function_button_code
	bsf memory_playback_manual_exit
	endif
	return

	endif ;enhanced_memory_support

; ------------------

; ------------------

	ifndef enhanced_memory_support

play_memory ; regular version without enhanced memory support

	; this subroutine plays one of the CW memories
	; parameters:
	; play_memory = number of memory to play (first one = 0)

	ifdef include_function_button_code
	bcf memory_playback_manual_exit			; clear flag to tell calling subroutines if user manaully exited
	endif

	; get parameters for this memory
	movlw eeprom_memory_locations			; get the *address* of memory location array
	movwf FSR
	movfw memory_number						; add for the appropriate memory location
	addwf FSR, F
	movfw INDF
	movwf temp_memory_eeprom_pointer		; initialize the live eeprom pointer
	; read the cw_unit count for this memory
	call read_eeprom
	movwf cw_unit_count

	; check for blank memory
	movlw 0xFF
	subwf cw_unit_count, W	; is the cw_unit count 0xFF ?
	btfss STATUS, Z
	goto byte_loop			; no, skip over this
	ifdef include_funky_beeps_code
	call low_beep
	else
	bsf sending_dit			; yes, send three dits and blow out
	bcf key_tx_active
	call send_dit_or_dah
	call send_dit_or_dah
	call send_dit_or_dah
	bsf key_tx_active
	endif ;include_funky_beeps_code
	return

byte_loop
	; get a byte from eeprom
	incf temp_memory_eeprom_pointer, F	; move to the next byte
	movfw temp_memory_eeprom_pointer
	call read_eeprom
	movwf temp_memory
	movlw 0x04				; four cw_units in a byte
	movwf temp_memory_mask	; use temp_memory_mask as a cw_unit within a byte counter
cw_unit_loop
	; TODO - exit if a switch or paddle is hit

	rlf temp_memory, F
	btfsc STATUS, C
	goto first_bit_1
;first_bit_0
	rlf temp_memory, F
	btfsc STATUS, C
	goto cw_unit_01
;cw_unit_00	- 6 cw_unit space
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	call loop_cw_unit
	goto next_cw_unit_setup
first_bit_1
	rlf temp_memory, F
	btfsc STATUS,C
	goto cw_unit_11
;cw_unit_10 - 2 cw_unit space
	call loop_cw_unit
	call loop_cw_unit
	goto next_cw_unit_setup
cw_unit_11 ; dah
	bcf sending_dit
	call send_dit_or_dah
	goto next_cw_unit_setup
cw_unit_01
	bsf sending_dit
	call send_dit_or_dah
next_cw_unit_setup
	decf cw_unit_count, F
	btfsc STATUS, Z				; did we do the last cw_unit ?
	return						; yes, blow out
    btfsc in_beacon_mode
	goto skip_button_check
	call check_for_button_hit
	sublw 0x01
	btfsc STATUS,Z
	goto button_hit_loop
skip_button_check
	decf temp_memory_mask, F	; no
	btfsc STATUS, Z				; did we do the last cw_unit in this byte ?
	goto byte_loop				; yes, grab another byte
	goto cw_unit_loop			; no, grab another cw_unit in this byte
	return

button_hit_loop
	call loop_check_for_button_hit
	ifdef include_function_button_code
	bsf memory_playback_manual_exit
	endif
	return

	endif ;enhanced_memory_support

; ------------------

program_memory_0

	clrf memory_number
	call program_memory

	return

; ------------------

program_memory_1

	movlw 0x01
	movwf memory_number
	call program_memory

	return

; ------------------

program_memory_2

	movlw 0x02
	movwf memory_number
	call program_memory

	return

;	endif

; ------------------

playback_memory_0
	clrf memory_number
	call play_memory
	return

; ------------------

playback_memory_1
	movlw 0x01
	movwf memory_number
	call play_memory
	return

; ------------------

playback_memory_2
	movlw 0x02
	movwf memory_number
	call play_memory
	return

; ---------------------------------------------

	ifdef multi_memory_page_support
	org 0800h
	endif

;----------------------------------------------

check_mode_button

	banksel PORTA
	pagesel $

	ifdef include_nnnnn_code
	; see if ditdah_history time has expired
	; TODO - make this driven by speed_wpm.  This may get falsely triggered at higher speeds
	decf ditdah_history_timer1, F
	btfsc STATUS, Z
	clrf ditdah_history1
	; check ditdah_history for NNNNN
	movfw ditdah_history1
	sublw b'10101010'		; check for dit, dah, dit, dah, dit, dah, dit, dah
	btfss STATUS, Z
	goto check_the_mode_switch ; we didn't find it, check the mode button
	movfw ditdah_history2	; we did find it, check the next byte
	andlw b'11000000'		; apply a bit mask so we get just two bits
	sublw b'10000000'		; check for dit, dah
	btfsc STATUS, Z
	goto loop_while_mode_pressed	; if we found it, go into command mode
	endif	; include_nnnnn_code

check_the_mode_switch
	btfsc	mode_switch		; if mode switch is pressed (0), keep going
	return					; otherwise there's nothing to do, blow out of here

loop_while_mode_pressed

	ifdef include_cw_rx_practice_code
	call get_random_number16	; while we're not doing anything, randomize
	endif

	ifdef include_mode_button_hold_code		; ---- special shortcuts by holding the mode button ----
	ifdef include_function_button_code
	btfss switch0
	goto call_program_memory_0_hold
	btfss switch1
	goto call_program_memory_1_hold
	btfss switch2
	goto call_program_memory_2_hold
	endif ;include_function_button_code
	btfss dit
	goto speed_mode_increment
	btfss dah
	goto speed_mode_decrement
	goto no_other_buttons_pressed

call_program_memory_0_hold
	errorlevel -306
	pagesel wait_for_mode_switch_release
	call wait_for_mode_switch_release
	pagesel program_memory_0
	call program_memory_0
	errorlevel +306
	goto blow_out_of_here_no_bk

call_program_memory_1_hold
	errorlevel -306
	pagesel wait_for_mode_switch_release
	call wait_for_mode_switch_release
	pagesel program_memory_1
	call program_memory_1
	errorlevel +306
	goto blow_out_of_here_no_bk

call_program_memory_2_hold
	errorlevel -306
	pagesel wait_for_mode_switch_release
	call wait_for_mode_switch_release
	pagesel program_memory_2
	call program_memory_2
	errorlevel +306
	goto blow_out_of_here_no_bk

quick_speed_mode_check
	btfss dit
	goto speed_mode_increment
	btfss dah
	goto speed_mode_decrement
	btfss mode_switch
	goto quick_speed_mode_check			; mode button is still depressed, keep looping
	errorlevel -306
	pagesel write_speed_wpm_to_eeprom
	call write_speed_wpm_to_eeprom		; mode button not pressed, save the speed setting and blow out
	errorlevel +306
	goto blow_out_of_here_no_bk

speed_mode_increment
	bcf key_tx_active
	errorlevel -306
	pagesel increment_wpm
	call increment_wpm
	errorlevel +306
	bsf sending_dit
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	goto quick_speed_mode_check

speed_mode_decrement
	bcf key_tx_active
	errorlevel -306
	pagesel decrement_wpm
	call decrement_wpm
	errorlevel +306
	bsf sending_dit
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	goto quick_speed_mode_check

no_other_buttons_pressed					; ---- end of mode button hold shortcuts ----
	endif ;include_mode_button_hold_code
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms			; button debounce time
	errorlevel +306
	pagesel $
	btfss	mode_switch			; wait until switch is released
	goto loop_while_mode_pressed
	banksel PORTA
	bcf key_tx_active			; disable tx keying line

	bcf expert_commands_on		; initialize some flags

	ifdef include_freq_counter_code
	bcf command_mode_v_cmd
	bcf command_mode_k_cmd
	endif ;include_freq_counter_code

	ifdef include_m_and_h_cmd_code
	bcf command_mode_m_cmd
	bcf command_mode_h_cmd
	endif ;include_m_and_h_cmd_code

	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel boop_beep
	call boop_beep
	errorlevel +306
	else
	bsf sending_dit				; send dit to announce command mode
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	endif

initialize_command_mode
	pagesel $
	banksel PORTA
	bcf paddle_was_hit
	clrf command_mode_buffer1
	clrf command_mode_buffer2

	; command_mode_buffer1 and 2 hold the dits and dahs that are sent in command mode
	; each element (dit or dah) takes up two bits
	; dit(01)/dah(11) -&gt; command_mode_buffer1 -&gt; command_mode_buffer2
	; so B equals 01010111 00000000
	;    , equals 11110101 11110000

	; now, the real stuff begins

init_command_mode_loop_vars

	; TODO - calculate directly using wpm

	movfw cw_unit_counter+1				; initialize command mode loop variables
	movwf command_mode_loop_count1
	movfw cw_unit_counter+0
	movwf command_mode_loop_count2
	movlw 0x01
	movwf command_mode_loop_count3

user_input_loop_mode		; ---- collect the user's command ---
	pagesel $
	banksel PORTA
	btfss mode_switch		; if mode switch is pressed, get out of command mode
	goto loop_while_mode_pressed_exit
	errorlevel -306
	pagesel check_paddles
	call check_paddles
	errorlevel +306
	pagesel $
	banksel PORTA
	btfsc dit_buffer		; was dit pressed ?
	goto dit_pressed
	btfsc dah_buffer		; was dah pressed ?
	goto dah_pressed
	btfss paddle_was_hit	; has a paddle been hit already ?
	goto user_input_loop_mode	; if not, loop back without decrementing counters
	decf command_mode_loop_count1, F
	btfss STATUS, Z
	goto user_input_loop_mode
	decf command_mode_loop_count2, F
	btfss STATUS, Z
	goto user_input_loop_mode
	decf command_mode_loop_count3, F
	btfss STATUS, Z
	goto user_input_loop_mode
figure_out_the_command
	; figure out what command was sent
	movlw 0x04
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	banksel PORTA
	pagesel $
	ifdef include_freq_counter_code
	btfsc command_mode_v_cmd		; V commands (freq counter offset mode) are two characters
	goto  command_mode_v_commands	; go to a special handler for these
	btfsc command_mode_k_cmd		; K command is several chanracters
	goto  command_mode_k_commands	; go to a special handler for these
	endif ;include_freq_counter_code

	ifdef include_m_and_h_cmd_code
	btfsc command_mode_m_cmd		; M commands (freq counter offset mode) are two characters
	goto  command_mode_m_commands	; go to a special handler for these
	btfsc command_mode_h_cmd		; H command is several chanracters
	goto  command_mode_h_commands	; go to a special handler for these
	endif ;include_m_and_h_cmd_code

	movfw command_mode_buffer2
	btfss STATUS, Z					; is second buffer byte 0 ?
	goto more_than_four_elements	; no, go direct to the more than four element commands
									; yes, decode only the first byte

	; ---- one character, four element regular commands ----

	banksel PORTA
	pagesel $

	ifdef include_iambic_mode_code
	movlw b'11010000'			; a = iambic a mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto set_iambic_a_mode
	movlw b'01010111'			; b = iambic b mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto set_iambic_b_mode
	endif

	movlw b'01000000'			; e = activate expert commands
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto activate_expert_commands

	ifdef include_needless_feature_code
	movlw b'01111100'
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto easter_egg
	endif

	ifdef include_paddle_reverse_code
	movlw b'01110100'			; r = toggle paddle reverse
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_paddle_reverse_toggle
	endif

	movlw b'01010100'			; s = speed mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_speed_mode

	ifdef include_tune_mode_code
	movlw b'11000000'			; t = tuning mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_tune_mode
	endif ;include_tune_mode_code

	ifdef include_bug_code
	movlw b'11010100'			; u = toggle bug mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_toggle_bug_mode
	endif

	ifdef include_m_and_h_cmd_code
	movlw b'11110000'			; m = playback memory
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto command_mode_m_activate
	endif ;include_m_and_h_cmd_code

	ifdef include_cw_rx_practice_code
	movlw b'01111101'			; p = rx code practice
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_code_rx_practice_mode
	endif

	ifdef include_cw_tx_practice_code
	movlw b'11011111'			; q = send code practice
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_code_tx_practice_mode
	endif

	ifdef include_m_and_h_cmd_code
	movlw b'01010101'			; h = program memory
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto command_mode_h_activate
	endif ;include_m_and_h_cmd_code

	ifdef include_weighting_code
	movlw b'11110100'			; w = weighting adjust
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_weighting_adjust
	endif

	ifdef include_freq_counter_code
	movlw b'11110111'			; y = frequency counter
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_count_frequency
	endif

	ifdef include_beacon_code
	movlw b'01011111'			; z = play beacon once
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_play_beacon
	endif

	movlw b'11010111'			; x = exit
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto blow_out_of_here

	btfss expert_commands_on
	goto wtf

	;-------------------- below here is four element or less expert commands ----------------------------------

	ifdef include_calibration_code

	movlw b'01110111'			; c = calibration check
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_calibration_check_mode

	endif

	ifdef include_toggle_sidetone_on_off_code
	movlw b'01011100'			; d = toggle sidetone on/off
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_sidetone_on_off
	endif

	ifdef include_wavesidetone_code
	movlw b'01110101'			; f = sidetone frequency adjust
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_sidetone_freq_adj
	endif

	ifdef include_freq_counter_calib_code
	movlw b'01011101'			; l = frequency counter calibration
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_freq_counter_calib_mode
	endif ;include_freq_counter_calib_code

	ifdef include_freq_counter_code
	movlw b'11010101'			; v = V command (two character command)
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto command_mode_v_activate
	endif ;include_freq_counter_code

	ifdef include_freq_counter_code
	movlw b'11011100'			; k = K command (two character command)
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto command_mode_k_activate
	endif ;include_freq_counter_code

	goto wtf

	;-------------------- end of four element or less expert commands ----------------------------------

more_than_four_elements		;------------------ more than four element commands --------------------------

	banksel PORTA
	pagesel $

	movlw b'01010000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_question_command
	movlw b'01011111'			; ? = query speed
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_announce_speed_wpm

skip_over_question_command

	btfss expert_commands_on
	goto wtf

more_than_four_elements_expert	;---------------------------- more than four element expert commands -------------

	ifdef include_freq_counter_code
	movlw b'11110000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_comma_command
	movlw b'11110101'			; comma = toggle 8 digit/3 digit mode
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_toggle_digit_mode
skip_over_comma_command
	endif

	;--------------------- end of all commands ----------------------------

wtf								; I don't know what the hell you sent
	movlw b'01011111' 			; send ?
	movwf cw_char_to_send+0
	movlw b'01010000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	banksel PORTA
	pagesel $
	ifdef include_freq_counter_code
	bcf command_mode_v_cmd		; clear out any in progress multicharacter commands
	bcf command_mode_k_cmd
	endif ;include_freq_counter_code
	goto initialize_command_mode

	; ---- exit routines after commands ----

blow_out_of_here
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel beep_boop
	call beep_boop
	errorlevel +306
	banksel PORTA
	pagesel $
	else
	movlw b'11010101' 			; send BK in CW
	movwf cw_char_to_send+0
	;movlw b'11011100'
	movlw b'10110111'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	banksel PORTA
	pagesel $
	endif ;include_funky_beeps_code
blow_out_of_here_no_bk
	;set things back to normal
	bcf dit_buffer
	bcf dah_buffer
	bsf key_tx_active
	ifdef include_nnnnn_code
	clrf ditdah_history1
	clrf ditdah_history2
	endif
	return

loop_while_mode_pressed_exit
	btfss	mode_switch		; wait until switch is released
	goto loop_while_mode_pressed_exit
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	banksel PORTA
	;bsf key_tx_active
	pagesel blow_out_of_here
	goto blow_out_of_here

	; ---- handle dits and dahs from user ----

dit_pressed
	; sound a dit
	bsf sending_dit
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	banksel PORTA
	pagesel $
	bsf paddle_was_hit
	bcf dit_buffer
	pagesel add_to_buffer
	goto add_to_buffer

dah_pressed
	; sound a dah
	bcf sending_dit
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	banksel PORTA
	pagesel $
	bsf paddle_was_hit
	bcf dah_buffer
add_to_buffer
	bsf STATUS, C
	rrf command_mode_buffer1, F
	rrf command_mode_buffer2, F
	bcf STATUS, C	; if it's a dit, put a 0 in next
	btfss sending_dit
	bsf STATUS, C	; otherwise, put a 1 in next
	rrf command_mode_buffer1, F
	rrf command_mode_buffer2, F
	pagesel init_command_mode_loop_vars
	goto init_command_mode_loop_vars

	;--------------------- double character commands ---------------------------

	ifdef include_m_and_h_cmd_code
command_mode_m_commands
    ifdef include_eis_mh_cmd_code ;-----------------------New Code from W0ANM----------------------
	movlw b'01000000'			; me      EIS code uses e,i and s instead of 0,1, and 2
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_0
	movlw b'01010000'			; mi
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_1
	movlw b'01010100'			; ms
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_2
	goto wtf
	else ;include_eis_mh_cmd_code ;-----------------------Original Code--------------------------------
	movlw b'11000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_m0_command
	movlw b'11111111'			; m0
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_0
skip_over_m0_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_m1_command
	movlw b'11111111'			; m1
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_1
skip_over_m1_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto wtf
	movlw b'11111101'			; m2
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_playback_memory_2
	goto wtf
	endif ;include_eis_mh_cmd_code
	endif ;include_m_and_h_cmd_code

	ifdef include_m_and_h_cmd_code
command_mode_h_commands
    ifdef include_eis_mh_cmd_code ;-----------------------New Code from W0ANM ----------------------
	movlw b'01000000'			; he           EIS code uses e,i and s instead of 0,1, and 2
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_0
	movlw b'01010000'			; hi
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_1
	movlw b'01010100'			; hs
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_2
	goto wtf
	else ;include_eis_mh_code     ;------------------------Original Code----------------------------------
	movlw b'11000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_h0_command
	movlw b'11111111'			; h0
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_0
skip_over_h0_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_h1_command
	movlw b'11111111'			; h1
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_1
skip_over_h1_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto wtf
	movlw b'11111101'			; h2
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_program_memory_2
	goto wtf
	endif ;include_eis_mh_cmd_code
	endif ;include_m_and_h_cmd_code

	ifdef include_freq_counter_code
command_mode_v_commands				; V commands : frequency counter offset mode
	movlw b'11000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_v0_command
	movlw b'11111111'			; v0 = no freq counter offset
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_v0_mode
skip_over_v0_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_v1_command
	movlw b'11111111'			; v1
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_v1_mode
skip_over_v1_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto skip_over_v2_command
	movlw b'11111101'			; v2
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_v2_mode
skip_over_v2_command
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfss STATUS,Z
	goto wtf
	movlw b'11110101'			; v3
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto call_v3_mode
	goto wtf
	endif ;include_freq_counter_code

	ifdef include_freq_counter_code
command_mode_k_commands				; K command : set frequency counter offset

	; the K command is K + digits, terminated with an X (Example: K123X

	movfw command_mode_buffer2	; check for the X which means time to blow out
	btfss STATUS,Z
	goto not_x_character
	movlw b'11010111'
	subwf command_mode_buffer1, W
	btfsc STATUS, Z
	goto exit_out_of_k_command

not_x_character
	movlw b'11000000'
	subwf command_mode_buffer2, W
	btfsc STATUS,Z
	goto k_6_7_8_9_0
	movlw b'01000000'
	subwf command_mode_buffer2, W
	btfsc STATUS,Z
	goto k_1_2_3_4_5
	goto wtf

k_1_2_3_4_5
	movlw b'11111111'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_1
	movlw d'1'
	goto add_to_freq_offset
skip_k_1
	movlw b'11111101'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_2
	movlw d'2'
	goto add_to_freq_offset
skip_k_2
	movlw b'11110101'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_3
	movlw d'3'
	goto add_to_freq_offset
skip_k_3
	movlw b'11010101'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_4
	movlw d'4'
	goto add_to_freq_offset
skip_k_4
	movlw b'01010101'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto wtf
	movlw d'5'
	goto add_to_freq_offset

k_6_7_8_9_0
	movlw b'01010101'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_6
	movlw d'6'
	goto add_to_freq_offset
skip_k_6
	movlw b'01010111'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_7
	movlw d'7'
	goto add_to_freq_offset
skip_k_7
	movlw b'01011111'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_8
	movlw d'8'
	goto add_to_freq_offset
skip_k_8
	movlw b'01111111'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto skip_k_9
	movlw d'9'
	goto add_to_freq_offset
skip_k_9
	movlw b'11111111'
	subwf command_mode_buffer1, W
	btfss STATUS, Z
	goto wtf
	movlw d'0'
	goto add_to_freq_offset

add_to_freq_offset

	movwf temp_w					; save w which is the number that was sent

	movlw d'4'						; rotate the BCD digits to the left four bits to make room for new digit
	movwf counter1
	bcf STATUS,C
add_to_freq_offset_loop
	rlf BCD2,F
	rlf BCD1,F
	rlf BCD0,F
	decf counter1,F
	btfss STATUS,Z
	goto add_to_freq_offset_loop

	movfw temp_w					; add the new char to lower nibble of BCD2
	addwf BCD2,F

	pagesel initialize_command_mode
	goto initialize_command_mode

exit_out_of_k_command
	errorlevel -306
	pagesel bcd2binary
	call bcd2binary				;convert what was sent (now in BCD) to two byte binary
	errorlevel +306
	movfw bcd2binary_binary_out+0
	movwf freq_offset_binary+0

	movfw bcd2binary_binary_out+1
	movwf freq_offset_binary+1

	errorlevel -306
	pagesel store_freq_offset_in_eeprom
	call store_freq_offset_in_eeprom ;save to EEPROM
	errorlevel +306
	bcf command_mode_k_cmd		; clear out K command mode flag
	goto acknowledgement_dit

	endif ;include_freq_counter_code

	;------------------ end of double character commands ---------------------------

	; ---- command mode activates ----

	; these commands are multiple character commands
	; bit flags are set to indicate we're in the middle of a multiple character command

	ifdef include_freq_counter_code
command_mode_v_activate					; V command : frequency counter offset mode
	bsf command_mode_v_cmd

	goto initialize_command_mode
	endif ;include_freq_counter_code

	ifdef include_freq_counter_code
command_mode_k_activate					; K command : set frequency counter offset
	bsf command_mode_k_cmd
	clrf BCD0
	clrf BCD1
	clrf BCD2
	goto initialize_command_mode
	endif ;include_freq_counter_code

	ifdef include_m_and_h_cmd_code
command_mode_m_activate					; M command : play memory #
	bsf command_mode_m_cmd
	goto initialize_command_mode
	endif ;include_m_and_h_cmd_code

	ifdef include_m_and_h_cmd_code
command_mode_h_activate					; H command : program memory #
	bsf command_mode_h_cmd
	goto initialize_command_mode
	endif ;include_m_and_h_cmd_code

activate_expert_commands				; E command : activates expert commands
	bsf expert_commands_on
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel high_beep
	call high_beep
	pagesel high_beep
	call high_beep
	pagesel high_beep
	call high_beep
	errorlevel +306
	banksel PORTA
	pagesel $
	else
	bsf sending_dit
	call send_dit_or_dah
	call send_dit_or_dah
	call send_dit_or_dah
	endif ;include_funky_beeps_code
	pagesel initialize_command_mode
	goto initialize_command_mode

	; --- calls to command subroutines ----

	ifdef include_bug_code
call_toggle_bug_mode
	call toggle_bug_mode
	call write_eeprom_settings1
	goto acknowledgement_dit
	endif

	ifdef include_freq_counter_code
call_count_frequency
	errorlevel -306
	pagesel count_frequency
	call count_frequency
	errorlevel +306
	goto initialize_command_mode

call_toggle_digit_mode
	call toggle_digit_mode
	errorlevel -306
	pagesel write_eeprom_settings1
	call write_eeprom_settings1
	errorlevel +306
	goto initialize_command_mode

call_v0_mode
	call v0_mode
	bcf command_mode_v_cmd
	goto acknowledgement_dit

call_v1_mode
	call v1_mode
	bcf command_mode_v_cmd
	goto acknowledgement_dit

call_v2_mode
	call v2_mode
	bcf command_mode_v_cmd
	goto acknowledgement_dit

call_v3_mode
	call v3_mode
	bcf command_mode_v_cmd
	goto acknowledgement_dit
	endif ;include_freq_counter_code

call_speed_mode
	errorlevel -306
	pagesel speed_mode
	call speed_mode
	errorlevel +306
	errorlevel -306
	pagesel announce_speed_wpm
	call announce_speed_wpm
	errorlevel +306
	goto initialize_command_mode

call_announce_speed_wpm
	errorlevel -306
	pagesel announce_speed_wpm
	call announce_speed_wpm
	errorlevel +306
	goto initialize_command_mode

	ifdef include_tune_mode_code

call_tune_mode
	call tune_mode
	goto initialize_command_mode

	endif ;include_tune_mode_code

	ifdef include_calibration_code
call_calibration_check_mode
	call calibration_check_mode
	goto initialize_command_mode
	endif ; include_calibration_code

	ifdef include_m_and_h_cmd_code
call_program_memory_0
	errorlevel -306
	pagesel program_memory_0
	call program_memory_0
	errorlevel +306
	bcf command_mode_h_cmd
	goto initialize_command_mode

call_program_memory_1
	errorlevel -306
	pagesel program_memory_1
	call program_memory_1
	errorlevel +306
	bcf command_mode_h_cmd
	goto initialize_command_mode

call_program_memory_2
	errorlevel -306
	pagesel program_memory_2
	call program_memory_2
	errorlevel +306
	bcf command_mode_h_cmd
	goto initialize_command_mode

call_playback_memory_0
	errorlevel -306
	pagesel playback_memory_0
	call playback_memory_0
	errorlevel +306
	bcf command_mode_m_cmd
	goto initialize_command_mode

call_playback_memory_1
	errorlevel -306
	pagesel playback_memory_1
	call playback_memory_1
	errorlevel +306
	bcf command_mode_m_cmd
	goto initialize_command_mode

call_playback_memory_2
	errorlevel -306
	pagesel playback_memory_2
	call playback_memory_2
	errorlevel +306
	bcf command_mode_m_cmd
	goto initialize_command_mode
	endif ;include_m_and_h_cmd_code

	ifdef include_beacon_code
call_play_beacon
	errorlevel -306
	pagesel play_beacon
	call play_beacon
	errorlevel +306
	goto initialize_command_mode
	endif ;include_beacon_code

	ifdef include_iambic_mode_code
set_iambic_a_mode
	bcf iambic_b_mode
	errorlevel -306
	pagesel write_eeprom_settings1
	call write_eeprom_settings1
	errorlevel +306
	goto acknowledgement_dit

set_iambic_b_mode
	bsf iambic_b_mode
	errorlevel -306
	pagesel write_eeprom_settings1
	call write_eeprom_settings1
	errorlevel +306
	goto acknowledgement_dit
	endif ;include_iambic_mode_code

	ifdef include_wavesidetone_code
call_sidetone_freq_adj
	errorlevel -306
	pagesel sidetone_freq_adj
	call sidetone_freq_adj
	errorlevel +306
	goto initialize_command_mode
	endif ;include_wavesidetone_code

	ifdef include_toggle_sidetone_on_off_code
call_sidetone_on_off
	call sidetone_on_off
	errorlevel -306
	pagesel write_eeprom_settings1
	call write_eeprom_settings1
	errorlevel +306
	goto acknowledgement_dit
	endif ;include_toggle_sidetone_on_off_code

	ifdef include_weighting_code
call_weighting_adjust
	call weighting_adjust
	goto initialize_command_mode
	endif ;include_weighting_code

	ifdef include_paddle_reverse_code
call_paddle_reverse_toggle
	call paddle_reverse_toggle
	goto acknowledgement_dit
	endif ;include_paddle_reverse_code

	ifdef include_needless_feature_code
easter_egg
	movlw b'11011100' 			; K
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'01010111'			; 3
	movwf cw_char_to_send+0
	movlw b'11000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'11010000' 			; N
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'11110100' 			; G
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	goto initialize_command_mode
	endif ;include_needless_feature_code

	ifdef include_cw_rx_practice_code
call_code_rx_practice_mode
	call code_rx_practice_mode
	goto initialize_command_mode
	endif ;include_cw_rx_practice_code

	ifdef include_cw_tx_practice_code
call_code_tx_practice_mode
	call code_tx_practice_mode
	goto initialize_command_mode
	endif ;include_cw_tx_practice_code

	ifdef include_freq_counter_calib_code
call_freq_counter_calib_mode
	ifdef include_function_button_code
	call freq_counter_calib_mode
	endif ;include_function_button_code
	goto initialize_command_mode
	endif ;include_freq_counter_calib_code

	; ---- end of calls to command subroutines ----

acknowledgement_dit
	movlw 0x03
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	banksel PORTA
	bsf sending_dit
	errorlevel -306
	pagesel send_dit_or_dah
	call send_dit_or_dah
	errorlevel +306
	banksel PORTA
	pagesel initialize_command_mode
	goto initialize_command_mode

; ------------------ end of check_mode_button ---------------------------------------------------

	ifdef include_freq_counter_code
v0_mode
	bcf offset0
	bcf offset1
	return
v1_mode
	bcf offset0
	bsf offset1
	return
v2_mode
	bsf offset0
	bcf offset1
	return
v3_mode
	bsf offset0
	bsf offset1
	return
	endif ;include_freq_counter_code

; ------------------

	ifdef include_freq_counter_calib_code
	ifdef include_function_button_code
freq_counter_calib_mode
	bcf bit_temp					; store eight_digit_mode so it can be restored later
	btfsc eight_digit_mode
	bsf bit_temp
	bsf eight_digit_mode
freq_counter_calib_mode_loop
	btfsc switch0		; is freq button pressed ?
	goto freq_calib_check_paddles
	errorlevel -306
	pagesel count_frequency
	btfss switch1
	call count_frequency
	errorlevel +306
freq_calib_check_paddles
	btfss dit	; check dit and dah paddles
	goto decrement_freq_calib
	btfss dah
	goto increment_freq_calib
	btfsc mode_switch
	goto freq_counter_calib_mode_loop

	; write settings to eeprom
	movlw eeprom_freq_calib_high
	errorlevel -302
	banksel EEADR
	movwf EEADR
	errorlevel +302
	banksel PORTA
	movfw frequency_counter_calib+0
	errorlevel -302
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA
	errorlevel -306
	pagesel write_eeprom
	call write_eeprom
	errorlevel +306

	movlw eeprom_freq_calib_low
	errorlevel -302
	banksel EEADR
	movwf EEADR
	errorlevel +302
	banksel PORTA
	movfw frequency_counter_calib+1
	errorlevel -302
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA
	errorlevel -306
	pagesel write_eeprom
	call write_eeprom
	errorlevel +306

	; restore original eight_digit_mode value
	btfsc bit_temp
	return
	bcf eight_digit_mode
	return

decrement_freq_calib
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel high_beep
	call high_beep
	errorlevel +306
	else
	bsf sending_dit
	call send_dit_or_dah
	endif
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	movlw 0xff
	decf frequency_counter_calib+1, F
	subwf frequency_counter_calib+1, W	; did we roll over to FF ?
	btfsc STATUS,Z
	decf frequency_counter_calib+0, F
	goto freq_counter_calib_mode

increment_freq_calib
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel low_beep
	call low_beep
	errorlevel +306
	else
	bsf sending_dit
	call send_dit_or_dah
	endif
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	incfsz frequency_counter_calib+1, F
	goto freq_counter_calib_mode
	incf frequency_counter_calib+0, F
	goto freq_counter_calib_mode

	endif ;include_function_button_code
	endif ;include_freq_counter_calib_code

; ------------------

	ifdef include_freq_counter_code

toggle_digit_mode
	btfsc eight_digit_mode
	goto go_to_three_digit_mode
	bsf eight_digit_mode
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel high_beep
	call high_beep
	errorlevel +306
	else
	bsf sending_dit
	call send_dit_or_dah
	call send_dit_or_dah
	endif ;include_funky_beeps_code
	return

go_to_three_digit_mode
	bcf eight_digit_mode
	ifdef include_funky_beeps_code
	errorlevel -306
	pagesel low_beep
	call low_beep
	errorlevel +306
	else
	bsf sending_dit
	call send_dit_or_dah
	call send_dit_or_dah
	endif ;include_funky_beeps_code
	return

	endif ;include_freq_counter_code

; ------------------

	ifdef include_bug_code

toggle_bug_mode
	btfsc bug_mode_on
	goto clear_bug_mode
	bsf bug_mode_on
	return

clear_bug_mode
	bcf bug_mode_on
	return

	endif ;include_bug_code

; ------------------

	ifdef include_cw_tx_practice_code

code_tx_practice_mode

	bsf sending_dit
	call send_dit_or_dah

code_tx_practice_mode_loop
	errorlevel -306
	pagesel check_paddles
	call check_paddles					; check if the paddles are active and fill buffers as needed
	errorlevel +306
	call send_buffer					; send any dits and dahs if the buffers have stuff

	btfsc mode_switch
	goto code_tx_practice_mode_loop

code_tx_mode_sw_loop
	btfss mode_switch
	goto code_tx_mode_sw_loop

	bcf dit_buffer
	bcf dah_buffer
	call pause_100_ms

	bsf sending_dit
	call send_dit_or_dah

	return

	endif ;include_cw_tx_practice_code

; ------------------

	ifdef include_cw_rx_practice_code

get_random_number16
	rlf randomnum+0,W
	xorwf randomnum+0,W
	movwf temp_w
	rlf temp_w, F
	movfw temp_w
	swapf randomnum+0, F
	swapf randomnum+1,W
	bcf STATUS,C
	movwf temp_w
	rlf temp_w, F
	movfw temp_w
	xorwf randomnum+0,W
	swapf randomnum+0, F
	andlw 0x01
	rlf randomnum+1, F
	xorwf randomnum+1, F
	rlf randomnum+0, F
	return

; ------------------

code_rx_practice_mode

	clrf BCD0		; used to keep track of number of characters sent in a group

code_rx_practice_loop

	;check paddles and mode button
	call check_for_button_hit
	sublw 0x01
	btfsc STATUS,Z
	goto code_rx_practice_exit

	call get_random_number16	; get random number
	movfw randomnum
	movwf temp_memory
	movlw b'00111111'			; get it under 65
	andwf temp_memory, F		; with a bit mask
	movlw d'34'					; upper limit of the number we want
	subwf temp_memory, W		; test it
	btfsc STATUS,C				; is result negative ?
	goto code_rx_practice_loop		; number wasn't within limit, get another

	movfw temp_memory			; double the validated random number
	addwf temp_memory, F		;  so we're in the right spot in the lookup table

	;send code
	movfw temp_memory
	call cw_table				; get the first byte of code
	movwf cw_char_to_send+0
	movfw temp_memory
	addlw 0x01					; increment it to get the next byte
	call cw_table
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306

	; pause after five character group
	incf BCD0,F
	movfw BCD0
	sublw 0x05
	btfss STATUS,Z
	goto code_rx_practice_loop
	call loop_6_cw_units
	clrf BCD0
	goto code_rx_practice_loop

code_rx_practice_exit
	call loop_check_for_button_hit		; loop until all buttons are off
	return

	endif ;include_cw_rx_practice_code
; ------------------

	ifdef include_paddle_reverse_code

paddle_reverse_toggle

	btfss paddle_reverse
	goto set_paddle_reverse
	bcf paddle_reverse
	return
set_paddle_reverse
	bsf paddle_reverse
	return

	endif ;include_paddle_reverse_code

; ------------------

	ifdef include_weighting_code

weighting_adjust
	bsf sending_dit
	call send_dit_or_dah
	bcf sending_dit
	call send_dit_or_dah
	btfss dit				; dit is pressed, dah is not
	btfss dah
	goto skip_decrease_weight
	call decrease_weight
	goto weighting_adjust
skip_decrease_weight
	btfss dah				; dah is pressed, dit is not
	btfss dit
	goto skip_increase_weight
	call increase_weight
	goto weighting_adjust
skip_increase_weight
	btfss mode_switch		; if switch is pressed, get out of loop
	goto loop_while_pressed3
	btfss dit				; if both paddles are pressed, this also exits
	btfsc dah
	goto weighting_adjust

loop_while_pressed3			; wait for mode_switch, dit and dah to be released
	call pause_100_ms
	btfss mode_switch
	goto loop_while_pressed3
	btfss dit
	goto loop_while_pressed3
	btfss dah
	goto loop_while_pressed3

	; clear buffers
	bcf dit_buffer
	bcf dah_buffer
	return

increase_weight
	movfw speed_wpm_dah
	sublw d'4'			; 5 wpm lower limit
	btfsc STATUS, C		;if 4 - speed_wpm &gt; 0, return and do not decrement
	return
	decf speed_wpm_dah, F
	call calculate_cw_unit_values
	return

decrease_weight
	movfw speed_wpm_dah
	sublw d'60'			; 60 wpm upper limit
	btfss STATUS, C		;if 60 - speed_wpm &lt; 0, return and do not increment
	return
	incf speed_wpm_dah, F
	call calculate_cw_unit_values
	return

	endif	;include_weighting_code

; ------------------

	ifdef include_toggle_sidetone_on_off_code
sidetone_on_off
	errorlevel -306
	pagesel clear_sidetone_off_during_tx
	btfsc sidetone_off_during_tx
	goto clear_sidetone_off_during_tx
	errorlevel +306
	bsf sidetone_off_during_tx
	return
clear_sidetone_off_during_tx
	bcf sidetone_off_during_tx
	return
	endif ;include_toggle_sidetone_on_off_code

; ------------------

	ifdef include_wavesidetone_code

freq_up

	movfw wavesidetone_counter_setting
	sublw d'19'			; lower limit
	btfss STATUS, Z
	decf wavesidetone_counter_setting, F
	return

	endif

; ------------------

	ifdef include_wavesidetone_code

freq_down

	movfw wavesidetone_counter_setting
	sublw d'77'			;  upper limit
	btfss STATUS, Z
	incf wavesidetone_counter_setting, F
	return

	endif

; ------------------

	ifdef include_wavesidetone_code

sidetone_freq_adj

	bcf sending_dit
	bsf sidetone

sidetone_loop
	errorlevel -306
	pagesel loop_cw_unit
	call loop_cw_unit
	errorlevel +306
	btfss dit				; dit is pressed, dah is not
	btfss dah
	goto skip_freq_up
	call freq_up
	goto sidetone_loop
skip_freq_up
	btfss dah				; dah is pressed, dit is not
	btfss dit
	goto skip_freq_down
	call freq_down
	goto sidetone_loop
skip_freq_down
	errorlevel -306
	pagesel loop_while_pressed_freq_adj
	btfss mode_switch		; if switch is pressed, get out of loop
	goto loop_while_pressed_freq_adj
	errorlevel +306
	btfss dit				; if both paddles are pressed, this also exits
	btfsc dah
	errorlevel -306
	pagesel sidetone_loop
	goto sidetone_loop
	errorlevel +306
loop_while_pressed_freq_adj			; wait for mode_switch, dit and dah to be released
	bcf sidetone
	bcf wavesidetone
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	btfss mode_switch
	goto loop_while_pressed_freq_adj
	btfss dit
	goto loop_while_pressed_freq_adj
	btfss dah
	goto loop_while_pressed_freq_adj

	; write wavesidetone freq to eeprom
	movlw eeprom_wavesidetone_setting
	errorlevel -302
	banksel EEADR
	movwf EEADR
	errorlevel +302
	banksel PORTA
	movfw wavesidetone_counter_setting
	errorlevel -302
	banksel EEDATA
	movwf EEDATA
	errorlevel +302
	banksel PORTA
	errorlevel -306
	pagesel write_eeprom
	call write_eeprom
	errorlevel +306

	bcf dit_buffer
	bcf dah_buffer

	return

	endif

; ------------------

	ifdef include_beacon_code

check_beacon_mode

	; see if we should go into infinite loop beacon mode

	btfsc dit				; if dit is keyed, go into beacon loop
	return

	bsf in_beacon_mode

	ifdef include_beacon_qrss_code	; initialize beacon cycle counter if qrss ids are enabled
	movlw beacon_qrss_cycles
	movwf beacon_cycle
	endif ;include_beacon_qrss_code

beacon_mode_loop

	; loop through the beacon continuosly

	ifdef include_txwake_line_code
	bsf txwake
	;endif
	movlw d'3'				; wait a bit to allow tx to come up
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	endif

	call play_beacon		; run the beacon

	ifdef include_txwake_line_code
	movlw d'4'				; txwake hang time
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	;ifdef include_txwake_line_code
	bcf txwake
	endif

	ifdef include_beacon_serial_ctrl_code
	call check_serial_beacon_control
	endif ;include_beacon_serial_ctrl_code

	ifdef include_beacon_sleep_time
	movlw beacon_sleep_time_100ms				; beacon sleep time
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	endif ;include_beacon_sleep_time

	goto beacon_mode_loop

	endif ;include_beacon_code

; ------------------

	ifdef include_beacon_serial_ctrl_code

check_serial_beacon_control
	btfsc PIR1,OERR		; did we overrun the buffer?
	goto handle_ser_beacon_overrun
	btfsc PIR1,FERR		; did we have a frame error?
	goto handle_ser_beacon_frame_error
	btfss PIR1,RCIF		; do we have something in the buffer?
	return				; no, blow out of here
	movfw RCREG			; pull byte out of the serial rx FIFO
	sublw d'48'			; was a zero sent?
	btfsc STATUS,Z
	goto beacon_sleep_loop	; yes, sleep in a loop
	return					; no, get out of here

handle_ser_beacon_overrun
	movfw RCREG
	return

handle_ser_beacon_frame_error
	bcf RCSTA,CREN
	bsf RCSTA,CREN
	return

beacon_sleep_loop
	btfsc PIR1,OERR		; did we overrun the buffer?
	call handle_ser_beacon_overrun
	btfsc PIR1,FERR		; did we have a frame error?
	call handle_ser_beacon_frame_error
	btfss PIR1,RCIF		; do we have something in the buffer?
	goto beacon_sleep_loop
	movfw RCREG			; pull byte out of the serial rx FIFO
	sublw d'49'			; was a one sent?
	btfsc STATUS,Z
	return					; yes, get out of here
	goto beacon_sleep_loop	; no, continue in loop

	endif ;include_beacon_serial_ctrl_code

; ------------------

	ifdef include_fox_code

check_fox_mode

	; see if we should go into infinite loop fox beacon mode

	btfsc dit				; if dit is keyed, check mode button
	return
	btfsc mode_switch		; if mode_switch is low, go into fox beacon mode
	return

fox_mode_loop
	ifdef include_txwake_line_code
	bsf txwake
	endif
	movlw d'3'				; wait a bit to allow tx to come up
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	call play_beacon		; run the beacon
	movlw d'4'				; txwake hang time
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	ifdef include_txwake_line_code
	bcf txwake
	endif
	movlw d'10'				; beacon sleep time
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	goto fox_mode_loop

	endif ;include_fox_code

; ------------------

	ifdef include_beacon_code

play_beacon

	; this subroutine plays the beacon once

	pagesel send_vvv_de_id
	errorlevel -306
	call send_vvv_de_id
	errorlevel +306

	ifdef include_beacon_serial_ctrl_code
	call check_serial_beacon_control
	endif ;include_beacon_serial_ctrl_code

    ifdef include_beacon_play_mem_1_code
	errorlevel -306
	pagesel playback_memory_1
	call playback_memory_1
	errorlevel +306
	ifdef include_beacon_serial_ctrl_code
	call check_serial_beacon_control
	endif ;include_beacon_serial_ctrl_code
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	errorlevel +306
	;pagesel send_vvv_de_id
	;errorlevel -306
	;call send_vvv_de_id
	;errorlevel +306
	ifdef include_beacon_serial_ctrl_code
	call check_serial_beacon_control
	endif ;include_beacon_serial_ctrl_code
	endif ;include_beacon_play_mem_1_code

    ifdef include_beacon_play_mem_2_code
	errorlevel -306
	pagesel playback_memory_2
	call playback_memory_2
	errorlevel +306
	ifdef include_beacon_serial_ctrl_code
	call check_serial_beacon_control
	endif ;include_beacon_serial_ctrl_code
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	errorlevel +306
	;pagesel send_vvv_de_id
	;errorlevel -306
	;call send_vvv_de_id
	;errorlevel +306
	ifdef include_beacon_serial_ctrl_code
	pagesel check_serial_beacon_control
	errorlevel -306
	call check_serial_beacon_control
	errorlevel +306
	endif ;include_beacon_serial_ctrl_code
    endif ;include_beacon_play_mem_2_code

	ifdef include_beacon_qrss_code
	decf beacon_cycle,F
	btfss STATUS,Z
	goto no_qrss_id_this_time
	movfw speed_wpm					; store the normal wpm value in temp register
	movwf temp
	ifdef include_weighting_code
	movfw speed_wpm_dah
	movwf speed_wpm_dah_beacon_temp
	movlw beacon_qrss_speed			; shift to qrss speed
	movwf speed_wpm_dah
	endif ;include_weighting_code
	movlw beacon_qrss_speed
	movwf speed_wpm
	pagesel calculate_cw_unit_values
	call calculate_cw_unit_values
	pagesel playback_memory_0
	call playback_memory_0		; send callsign
	pagesel loop_cw_unit
	call loop_cw_unit
	pagesel loop_cw_unit
	call loop_cw_unit
	pagesel loop_cw_unit
	call loop_cw_unit
	errorlevel +306
	movfw temp					; restore normal wpm value
	movwf speed_wpm
	ifdef include_weighting_code
	movfw speed_wpm_dah_beacon_temp
	movwf speed_wpm_dah
	endif ;include_weighting_code
	pagesel calculate_cw_unit_values
	call calculate_cw_unit_values
	movlw beacon_qrss_cycles
	movwf beacon_cycle
no_qrss_id_this_time
	endif ;include_beacon_qrss_code

	return

send_vvv_de_id
	movlw b'00000000'
	movwf cw_char_to_send+1
	movlw b'01010111' 			; V
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'01010111' 			; V
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'01010111' 			; V
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'11010100' 			; D
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'01000000' 			; E
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	pagesel playback_memory_0
	call playback_memory_0		; send callsign
	pagesel loop_cw_unit
	call loop_cw_unit
	pagesel loop_cw_unit
	call loop_cw_unit
	pagesel loop_cw_unit
	call loop_cw_unit
	errorlevel +306
	movlw b'11010111'			; /
	movwf cw_char_to_send+0
	movlw b'01000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'11010101' 			; B
	movwf cw_char_to_send+0
	movlw b'00000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	pagesel loop_6_cw_units
	call loop_6_cw_units
	errorlevel +306
	return

	endif ;include_beacon_code

; ------------------

	ifdef include_function_button_code

playback_memory_loop_delay
	movlw initial_memory_repeat_delay
	movwf temp_memory
	bcf memory_playback_manual_exit

playback_memory_loop
	errorlevel -306
	pagesel check_for_button_hit
	call check_for_button_hit		; check for user exit
	errorlevel +306
	sublw 0x01
	btfsc STATUS,Z
	goto playback_memory_loop_user_exit
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	decfsz temp_memory,F
	goto playback_memory_loop
	return

playback_memory_loop_user_exit
	bsf memory_playback_manual_exit
	return

	endif ;include_function_button_code

; ------------------

	ifdef include_aux_line_code

toggle_aux_line

	; this changes the state of the aux line
	btfss aux
	goto set_aux_high
	bcf aux
	return
set_aux_high
	bsf aux
	return

	endif ;include_aux_line_code

; ------------------

	ifdef include_function_button_code

check_function_buttons

	; this checks if any memory buttons are pressed
	; single hit = play memory

	ifdef include_call_cq_code
	bcf dit_hit
	endif ;include_call_cq_code

	;check if any button lines are low
	btfss switch0
	goto a_function_button_is_pressed
	btfss switch1
	goto a_function_button_is_pressed
	btfss switch2
	goto a_function_button_is_pressed
	return								; no buttons pressed, blow out of here

a_function_button_is_pressed

	movlw b'00000111'
	movwf porta_temp

	movlw d'30'
	movwf count

function_button_read_loop			; this loop reads the switch lines over and over again to prevent bounce and misreads
	movfw PORTA						; get port a register
	andwf porta_temp, F				; and it with a temporary register
	decfsz count,F
	goto function_button_read_loop

	ifdef include_freq_counter_button_code
	ifdef include_freq_counter_code
	btfsc porta_temp,0							; freq counter button: switch0 = 0, switch1 = 0, switch2 = 1
	goto no_freq_counter_button_press
	btfsc porta_temp,1
	goto no_freq_counter_button_press
	btfss porta_temp,2
	goto no_freq_counter_button_press
	bcf key_tx_active
	call count_frequency
	bsf key_tx_active
	goto get_out
no_freq_counter_button_press
	endif ;include_freq_counter_code
	endif ;include_freq_counter_button_code

	ifdef include_aux_line_code
	btfsc porta_temp,0							; freq counter button: switch0 = 0, switch1 = 0, switch2 = 0
	goto aux_button_not_pressed
	btfsc porta_temp,1
	goto aux_button_not_pressed
	btfsc porta_temp,2
	goto aux_button_not_pressed
aux_button_wait_loop							; wait for all three lines to go high so we're sure the button has been let go
	btfss porta_temp,0
	goto aux_button_wait_loop
	btfss porta_temp,1
	goto aux_button_wait_loop
	btfss porta_temp,2
	goto aux_button_wait_loop
	call pause_100_ms
	call toggle_aux_line
	goto get_out
aux_button_not_pressed
	endif ;include_aux_line_code

	btfss switch0
	goto handle_switch0
	btfss switch1
	goto handle_switch1
	btfss switch2
	goto handle_switch2
	return

handle_switch0
	ifdef include_call_cq_code
	btfss dit					; was dit hit ?
	bsf dit_hit				; if so, set bit_temp
	endif ;include_call_cq_code
	incf count,F				; button hold check code
	btfsc switch0
	goto switch0_off_now
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	goto handle_switch0
switch0_off_now
	ifdef include_call_cq_code
	btfsc dit_hit				; check if dit was hit
	goto call_call_cq
	endif ;include_call_cq_code
	movlw 0x05
	subwf count,F
	btfsc STATUS,C 			; 5 or more 100 mS equals a button hold
	goto playback_memory_0_loop
	errorlevel -306
	pagesel playback_memory_0
	call playback_memory_0
	errorlevel +306
	goto get_out

playback_memory_0_loop
	errorlevel -306
	pagesel playback_memory_0
	call playback_memory_0					; play the memory once
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	call playback_memory_loop_delay			; do a delay of silence
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	goto playback_memory_0_loop

handle_switch1
	incf count,F				; button hold check code
	btfsc switch1
	goto switch1_off_now
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	goto handle_switch1
switch1_off_now
	movlw 0x05
	subwf count,F
	btfsc STATUS,C 			; 5 or more 100 mS equals a button hold
	goto playback_memory_1_loop
	errorlevel -306
	pagesel playback_memory_1
	call playback_memory_1
	errorlevel +306
	goto get_out

playback_memory_1_loop
	errorlevel -306
	pagesel playback_memory_1
	call playback_memory_1					; play the memory once
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	call playback_memory_loop_delay			; do a delay of silence
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	goto playback_memory_1_loop

handle_switch2
	incf count,F				; button hold check code
	btfsc switch2
	goto switch2_off_now
	errorlevel -306
	pagesel pause_100_ms
	call pause_100_ms
	errorlevel +306
	goto handle_switch2
switch2_off_now
	movlw 0x05
	subwf count,F
	btfsc STATUS,C 			; 5 or more 100 mS equals a button hold
	goto playback_memory_2_loop
	errorlevel -306
	pagesel playback_memory_2
	call playback_memory_2					; play the memory once
	errorlevel +306
	goto get_out

playback_memory_2_loop
	errorlevel -306
	pagesel playback_memory_2
	call playback_memory_2					; play the memory once
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	call playback_memory_loop_delay			; do a delay of silence
	btfsc memory_playback_manual_exit		; check if the user exited
	goto get_out
	goto playback_memory_2_loop

get_out
	bcf dit_buffer
	bcf dah_buffer
	return

	ifdef include_call_cq_code
call_call_cq
	bsf send_cw_preemptible
	call call_cq
	bcf send_cw_preemptible
	goto get_out
	endif ;include_call_cq_mode

	endif ;include_function_button_code

; ------------------

	ifdef include_call_cq_code

call_cq

	bcf memory_playback_manual_exit
	call send_cq
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	call send_cq
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	call send_cq
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	movlw b'11010100'			; D
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	movlw b'01000000'			; E
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	pagesel playback_memory_0
	call playback_memory_0
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	pagesel playback_memory_0
	call playback_memory_0
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	pagesel playback_memory_0
	call playback_memory_0
	errorlevel +306
	btfsc memory_playback_manual_exit		; check if the user exited
	return
	errorlevel -306
	pagesel loop_6_cw_units
	call loop_6_cw_units
	errorlevel +306
	movlw b'01110111'			; AR
	movwf cw_char_to_send+0
	movlw b'01000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	return

send_cq
	movlw b'11011101'			; C
	movwf cw_char_to_send+0
	movlw b'00000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'11110111'			; Q
	movwf cw_char_to_send+0
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	return

	endif ;include_call_cq_mode

; ------------------

	ifdef include_freq_counter_code
	ifdef include_freq_counter_button_code
	ifdef include_function_button_code

check_freq_counter_mode

	; check if frequency button is pressed
	; if so, loop forever in frequency counter readout mode
	; (this subroutine is called at power up only)

	btfsc switch0
	return
	btfsc switch1
	return
	bcf key_tx_active
freq_counter_loop
	call count_frequency
	movlw d'5'
	errorlevel -306
	pagesel pause_w_100ms
	call pause_w_100ms
	errorlevel +306
	goto freq_counter_loop

	endif ;include_function_button_code
	endif ;include_freq_counter_button_code
	endif ;include_freq_counter_code

; ------------------
	ifdef include_txwake_line_code

check_txwake_clear_time

	;if there's stuff in a buffer, reset the timer
	btfsc dit_buffer
	goto reset_txwake_timer
	btfsc dah_buffer
	goto reset_txwake_timer
	decfsz txwakecounter+1, F
	return
	decfsz txwakecounter+0, F
	return
	bcf txwake
	return

reset_txwake_timer
	movlw HIGH _txwake_delay_time
	movwf txwakecounter+0
	movlw LOW _txwake_delay_time
	movwf txwakecounter+1
	return

	endif

; ------------------

check_startup_modes

	; see if any of the special startup modes have been invoked by the user at power up

	btfss dit
	btfsc dah
	goto check_other_modes
	goto reset_to_defaults				; if both dit and dah lines are low, reset to defaults
check_other_modes
	ifdef include_fox_code
	pagesel check_fox_mode
	errorlevel -306
	call check_fox_mode
	errorlevel +306
	endif
	errorlevel -306
	pagesel check_straight_key_mode
	call check_straight_key_mode		; check if the user wants to go into brain dead straight key mode
	errorlevel +306
	ifdef include_beacon_code
	errorlevel -306
	pagesel check_beacon_mode
	call check_beacon_mode				; check if the user wants to go into beacon mode
	errorlevel +306
	endif ;include_beacon_code
	ifdef include_freq_counter_button_code
	ifdef include_freq_counter_code
	ifdef include_function_button_code
    call check_freq_counter_mode      	; check if the user is holding the freq button and go into freq count loop mode
	endif ;include_function_button_code
	endif ;include_freq_counter_code
	endif ;include_freq_counter_button_code

	return								; no startup mode is needed, blow out of here

; ------------------

reset_to_defaults
	btfss dit							; loop until one paddle is released
	goto reset_to_defaults
	btfss dah
	goto reset_to_defaults
	errorlevel -306
	pagesel initialize_eeprom_first_time
	call initialize_eeprom_first_time
	pagesel initialize
	call initialize
	errorlevel +306
	return

; ------------------

	ifdef include_freq_counter_code

	; 1.  Read the frequency on pin RA4 for 250 mS.  The signal goes through the prescaler /32
	; 2.  Multiply measurement by 128  [ /32 (prescaler) + /4 (250 mS sample time) ]
	; 3.  Convert the three byte binary reading to eight BCD digits
	; 4.  Calculate the reading by applying the offset if applicable
	; 5.  Readout the frequency in CW either in eight or three digit mode, with leading zero suppression

count_frequency

	errorlevel -302
	banksel OPTION_REG
	movlw b'00110100'
	; bit 5		: 1 = RA4 input
	; bit 4		: 0 = low to high transistion
	; bit 3		: 0 = prescaler assigned to TIMER0
	; bit 2-0	: set prescaler to 100 (/32)
	movwf OPTION_REG
	errorlevel +302
	banksel PORTA
	movfw frequency_counter_calib+1
	movwf counter1
	movfw frequency_counter_calib+0
	movwf counter2
	clrf freqcount+0
	clrf freqcount+1
	clrf freqcount+2
	bcf INTCON,T0IF
	clrf TMR0			; clear TIMER0 and prescaler
loop_250_ms
	movfw TMR0
	btfss INTCON,T0IF	; did TIMER0 overflow?
	goto no_timer0_overflow
	bcf INTCON,T0IF
	incfsz freqcount+1, F
	goto no_overflow_freqcount_1
	incf freqcount+0, F
	goto overflow_freqcount_1
no_timer0_overflow
	nop
	nop
	nop
no_overflow_freqcount_1
	nop
	nop
overflow_freqcount_1

	incf counter1, F
	btfsc STATUS,Z
	incf counter2, F
	btfss STATUS,Z
	goto loop_250_ms

loop_250_ms_done
	;movfw TMR0			; get timer0 as least significant byte
	movwf freqcount+2

	;multiple by 128 by rotating everything right 7 times
	movlw d'7'
	movwf count
multiply_loop
	bcf STATUS,C
	rlf freqcount+2, F
	rlf freqcount+1, F
	rlf freqcount+0, F
	decfsz count,F
	goto multiply_loop

	;convert 24 bit freqcount to 8 digit BCD
	bcf STATUS,C                ; clear the carry bit
	movlw d'24'
	movwf count
	clrf BCD0	; most significant
	clrf BCD1
	clrf BCD2
	clrf BCD3	; least significant
loop24
	rlf freqcount+2, F
	rlf freqcount+1, F
	rlf freqcount+0, F
	rlf BCD3,F
	rlf BCD2,F
	rlf BCD1,F
	rlf BCD0,F
	decfsz count, F
	goto adjDEC24
	goto calculate_the_reading
;	goto send_the_digits_in_cw
adjDEC24
	movlw BCD3
	movwf FSR
	call adjBCD24
	movlw BCD2
	movwf FSR
	call  adjBCD24
	movlw BCD1
	movwf FSR
	call adjBCD24
	movlw BCD0
	movwf FSR
	call adjBCD24
	goto loop24
adjBCD24
	movlw 0x03
	addwf INDF,W
	movwf temp_memory
	btfsc temp_memory,3         ; test if result &gt; 7
	movwf INDF
	movlw 0x30
	addwf INDF,W
	movwf temp_memory
	btfsc temp_memory,7         ; test if result &gt; 7
	movwf INDF               	; save as MSD
	return

calculate_the_reading

	; calculate the reading using the offset
	btfsc eight_digit_mode			; if we are in eight digit mode, there is no offset applied
	goto send_the_digits_in_cw
	btfsc offset0
	goto offset0_is_1
	btfss offset1
	goto send_the_digits_in_cw		; there is no offset activated, skip over
	pagesel calc_offset_minus_measurement
	errorlevel -306
	goto calc_offset_minus_measurement
	errorlevel +306
offset0_is_1
	pagesel calc_measurement_plus_offset
	errorlevel -306
	btfsc offset1
	goto calc_measurement_plus_offset
	pagesel calc_measurement_minus_offset
	goto calc_measurement_minus_offset
	errorlevel +306

send_the_digits_in_cw

	btfss eight_digit_mode	; are we in eight digit mode ?
	goto skip_BCD0			; no, skip over BCD0
	;send BCD0 H
	swapf BCD0,F
	movfw BCD0
	andlw b'00001111'
	btfsc STATUS,Z
	goto skip_BCD0H
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306

skip_BCD0H
	;send BCD0 L
	swapf BCD0,F
	movfw BCD0
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306

skip_BCD0
	btfsc eight_digit_mode
	goto no_BCD1H_zero_suppress
	movfw BCD1
	btfsc STATUS,Z
	goto BCD1_is_zeros
no_BCD1H_zero_suppress
	;send BCD1 H
	swapf BCD1,F
	movfw BCD1
	andlw b'00001111'
	pagesel send_w_BCD_in_cw
	btfss eight_digit_mode	; skip over zero suppresion if we're in 8 digit mode
	btfss STATUS,Z			; zero suppression
	errorlevel -306
	call send_w_BCD_in_cw
	errorlevel +306
skip_BCD1H
	;send BCD1 L
	swapf BCD1,F
	movfw BCD1
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306

BCD1_is_zeros

	swapf BCD2,F
	movfw BCD2
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306
	btfss eight_digit_mode
	return						; if we are in three digit mode, blow out of here
	swapf BCD2,F
	movfw BCD2
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306

	swapf BCD3,F
	movfw BCD3
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306
	swapf BCD3,F
	movfw BCD3
	andlw b'00001111'
	errorlevel -306
	pagesel send_w_BCD_in_cw
	call send_w_BCD_in_cw
	errorlevel +306

	return

	; these routines calculate the frequency readout when the offset is configured

	;   0     7  ,  0    4     0   ,  0     0    0		hertz
	;+-----+-----+-----+-----+-----+-----+-----+-----+
	;|BCD0H|BCD0L|BCD1H|BCD1L|BCD2H|BCD2L|BCD3H|BCD3L|
	;+-----+-----+-----+-----+-----+-----+-----+-----+
	;				|     |     |
	; 			 +-----+-----+-----+
	;			 |    freq_offset  |
	;            +-----+-----+-----+

calc_offset_minus_measurement

	clrf BCD0						; in this offset mode we the 10 Mhz and 1 Mhz digits
	errorlevel -306
	pagesel rotate_BCD012_right
	call rotate_BCD012_right		; rotate the BCD digits one position to the right
	errorlevel +306
	clrf BCD0
	errorlevel -306
	pagesel binary2bcd
	call bcd2binary					; convert the BCD measurement to binary
	errorlevel +306
	movfw freq_offset_binary+1		; store offset in temp registers
	movwf L_temp
	movfw freq_offset_binary+0
	movwf H_temp
	movfw bcd2binary_binary_out+1
	subwf L_temp,F
	btfss STATUS,C
	decf H_temp,F
	movfw binary2bcd_binary_in+0
	subwf H_temp,F
	movfw L_temp
	movwf binary2bcd_binary_in+1
	movfw H_temp
	movwf binary2bcd_binary_in+0
	errorlevel -306
	pagesel binary2bcd
	call binary2bcd
	pagesel rotate_BCD012_left
	call rotate_BCD012_left
	pagesel send_the_digits_in_cw
	goto send_the_digits_in_cw
	errorlevel +306

calc_measurement_minus_offset
	call rotate_BCD012_right		; rotate the BCD digits one position to the right
	errorlevel -306
	pagesel binary2bcd
	call bcd2binary					; convert the BCD measurement to binary
	errorlevel +306
	movfw freq_offset_binary+1
	subwf bcd2binary_binary_out+1,F
	btfss STATUS,C
	decf bcd2binary_binary_out+0,F
	movfw freq_offset_binary+0
	subwf bcd2binary_binary_out+0,F	;bcd2binary_binary_out becomes input parameters for binary2bcd routine
	errorlevel -306
	pagesel binary2bcd
	call binary2bcd
	pagesel rotate_BCD012_left
	call rotate_BCD012_left
	pagesel send_the_digits_in_cw
	goto send_the_digits_in_cw
	errorlevel +306

calc_measurement_plus_offset
	call rotate_BCD012_right		; rotate the BCD digits one position to the right
	errorlevel -306
	pagesel binary2bcd
	call bcd2binary					; convert the BCD measurement to binary
	errorlevel +306
	movfw freq_offset_binary+1
	addwf bcd2binary_binary_out+1,F
	btfsc STATUS,C
	incf bcd2binary_binary_out+0,F
	movfw freq_offset_binary+0
	addwf bcd2binary_binary_out+0,F	;bcd2binary_binary_out becomes input parameters for binary2bcd routine
	errorlevel -306
	pagesel binary2bcd
	call binary2bcd
	pagesel rotate_BCD012_left
	call rotate_BCD012_left
	pagesel send_the_digits_in_cw
	goto send_the_digits_in_cw
	errorlevel +306
	endif ;include_freq_counter_code

; ------------------

	ifdef include_freq_counter_code

rotate_BCD012_right
	clrf temp_w
	movlw d'4'
	movwf count
rotate_BCD012_right_loop
	bcf STATUS,C
	rrf BCD0,F
	rrf BCD1,F
	rrf BCD2,F
	rrf temp_w,F
	decf count,F
	btfss STATUS,Z
	goto rotate_BCD012_right_loop
	movfw temp_w		; store the original lower nibble of BCD2 in upper nibble of BCD0
	addwf BCD0,F
	return

	endif ;include_freq_counter_code

	ifdef include_rotateBCD012_left

; ------------------

rotate_BCD012_left

	;this rotates

	; BCD0 &lt;- BCD1 &lt;- BCD2 &lt;-
	;   |                    |
	;   +--------------------+

	; four bits

	clrf temp_w
	movlw d'4'
	movwf count
rotate_BCD012_left_loop
	bcf STATUS,C
	rlf BCD2,F
	rlf BCD1,F
	rlf BCD0,F
	rlf temp_w,F
	decf count,F
	btfss STATUS,Z
	goto rotate_BCD012_left_loop
	movfw temp_w		; store the upper nibble of BCD0 in lower nibble of BCD2
	addwf BCD2,F
	return

	endif ;include_rotateBCD012_left

; ------------------

	ifdef include_say_hi_code

say_hi
	;say hi
	bcf key_tx_active
	movlw b'01010101'			; H
	movwf cw_char_to_send+0
	movlw b'00000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	movlw b'01010000'			; I
	movwf cw_char_to_send+0
	movlw b'00000000'
	movwf cw_char_to_send+1
	errorlevel -306
	pagesel send_cw
	call send_cw
	errorlevel +306
	bsf key_tx_active
	return

    endif ;include_say_hi_code

; Main Program ------------------------------------------------------------

main_program_start						; this is where the whole shebang starts

	errorlevel -306
	pagesel initialize
	call initialize						; initialize registers, read from eeprom, etc.
	pagesel check_startup_modes
	call check_startup_modes			; see if the user is invoking a special startup mode
	errorlevel +306

	ifdef include_say_hi_code
	pagesel say_hi
    call say_hi
    endif ;include_say_hi_code

	ifdef include_funky_beeps_code		; this is for debugging / programming purposes
	ifdef include_debug_code			; do a beep at the beginning to make sure the programming worked
	errorlevel -306
	pagesel low_beep
	call beep_boop						; this is needed when using in circuit serial programming
	;call low_beep						; as there's no way to tell if code was successfully loaded
	pagesel high_beep
	call high_beep
	errorlevel +306
	endif ;include_debug_code
	endif ;include_funky_beeps_code

										; now we're done with all the startup stuff, let's send some CW
main_loop

	banksel PORTA						; for good measure

	errorlevel -306
	pagesel check_mode_button
	call check_mode_button				; check if the mode button is pressed and take care of it

	pagesel check_paddles
	call check_paddles					; check if the paddles are active and fill buffers as needed

	pagesel send_buffer
	call send_buffer					; send any dits and dahs if the buffers have stuff

	errorlevel +306

	ifdef include_function_button_code
	pagesel check_function_buttons
	errorlevel -306
	call check_function_buttons			; check if any of the function buttons are pressed and take care of them
	errorlevel +306
	endif ;include_function_button_code

	ifdef include_txwake_line_code
	pagesel check_txwake_clear_time
	errorlevel -306
	call check_txwake_clear_time
	errorlevel +306
	endif

	ifdef include_serial_cw_keyboard
	pagesel check_serial_cw_keyboard
	errorlevel -306
	call check_serial_cw_keyboard
	errorlevel +306
	endif ;include_serial_cw_keyboard

	pagesel main_loop
	goto main_loop						; lather, rinse, repeat

	end

; End Main Program --------------------------------------------------------