;
; $Header: E:/HAM/EVM/RCS/psk.asm 1.14 1997/10/23 13:43:08 dbraun Exp $
;
; $Log: psk.asm $
; Revision 1.14  1997/10/23 13:43:08  dbraun
; Added blinking of orange LED for received serial port data.
;
; Revision 1.13  1997/10/22 22:28:58  dbraun
; Added spying of DCD, and optional yellow LED for data flow indication
;
; Revision 1.12  1997/02/05 09:11:55  dbraun
; Change to DCD calculation to make it less sensitive when
; spectral asymmetry is not used.
;
; Revision 1.11  1997/01/22 11:01:22  dbraun
; Removed Hilbert Transform filtering of new transmitter output.
;
; Revision 1.10  1997/01/19 23:17:44  dbraun
; Added fudge factor for initialization of TxDigLevel for new xmtr.
;
; Revision 1.9  1997/01/19 20:09:22  dbraun
; Added new transmitter code.
;
; Revision 1.8  1997/01/19 18:02:32  dbraun
; Updated SPY code to more recent spec.
;
; Revision 1.7  1997/01/18 16:52:30  dbraun
; Tweaked some frequencies, etc. for my IC821.
;
; Revision 1.6  1996/12/16 22:27:19  dbraun
; Fixed up spectral asymettry on/off option, and reduced
; mike click rate for IC821.
;
; Revision 1.5  1996/12/08 10:15:23  dbraun
; added support for run-time patching of kiss txtail parameter.
;
; Revision 1.4  1996/12/06 22:12:29  dbraun
; Tweaked levels for IC821
;
; Revision 1.3  1996/11/25 08:06:47  dbraun
; Added assembly-time option for use of spectral asymmetry for DCD.
;
; Revision 1.2  1996/06/19 12:48:29  dbraun
; Added RCS headers
;
;

;FILE: BPSK1200.ASM, Last edition: 01-JAN-1996
;1200 bps BPSK modem, primary usage is for satellites like LO-19
;but can be used in simplex mode for HF or VHF packet.
;(c) 1995,1996 Pawel Jalocha, SP9VRC,
;e-mail: jalocha@chopin.ifj.edu.pl, sp9vrc@gw.sp9kby.ampr.org
;
;This software is not to be used for purposes other than amateur radio
;without a written permision of the author.
;Usage for profit or in commercial/military products is explicitly prohibited.

        page 132

        opt mu
        nolist
	include 'leonid'
	list

        scsjmp short    ;force short jumps for .if/.endi, .while/.endw, etc.

        title '1200 bps BPSK modem by SP9VRC'

;*************************************************************************
;compile-time constants, modem parameters


SampleFreq equ 9600.0

AliasFilterLen  equ 64  ;the length of the anti-alias/Hilbert transform filter
AliasLowFreq    equ  150.0 ;lower and upper edges of the input/output filters
AliasUppFreq    equ 3050.0 ;here we center the filters at 1600 Hz

HilbertDecimate equ 1   ;decimation ratio for the Hilbert transform.
                        ;1 => one Hilbert (complex) sample per each real sample
                        ;2 => bit rate falls by 2 without touching any
                        ;     other parameters. Note that we can decimate by 2
                        ;     without risks for any aliasing because we
                        ;     process complex samples.
                        ;higher than 2 can be done but you need to think well
                        ;about the anti-alias filters and the carrier freq.

SymbolSepar equ 8       ;8 samples between symbols => 9600/8=1200 baud
                        ;SymbolSepar should be an even number
                        ;If you change it, there is one place around line 450
                        ;to be manually changed.
SymbolShapeLen equ 2*SymbolSepar ;Symbol's shape is 16 samples long
                        ;For the current symbol's shape (Tr/RxSymbolShape)
                        ;SymbolShapeLen should be 2*SymbolSepar

UseNewTransmitter equ 1 ;Use the new N1OWU transmitter code instead of
			;the original PSK-like code.  This is only
			;for Manchester satellite uplinks.  If enabled,
			;the TxCarrier and TxCarSync are irrelevant.

TxCarrier  equ 1200.0   ;[Hz] transmitter carrier frequency.
                        ;For satellite uplink this should be 1200 Hz

TxCarSync  equ 1        ;Synchronize the carrier to the data rate.
                        ;This we need for the Manchester satellite uplink.
                        ;Enable this option only if the carrier frequency
                        ;is an exact multiply of the symbol rate like for
                        ;1200 bps and 1200 Hz.
                        ;If you try this BPSK modem on shortwave (no need
                        ;for Manchester encoding) I suggest that you set
                        ;TxCarrier around 1500-1700 Hz - then you must turn off
                        ;the TxCarSync.

TxContCarrier equ 0     ;1 => keep transmitter's carrier ON all the time.
                        ;0 => the carrier is ON only when the PTT is on.
                        ;     Beware of the TxTail being long enough !
                        ;If this is on, beware leaving the modem turned on
                        ;when working voice!!!

DefTxDelay    equ 20    ;default value for the txdelay (20 => 200 ms)
DefTxTail     equ 20    ;default value for the txtail (3 => 30 ms)
DefTxDuplex   equ 1     ;1 = full duplex mode (for satellite uplink)
                        ;0 = simplex mode (for HF or VHF packet)
SciTimerRate equ baud   ;interrupt rate of the SCI timer. This we need
                        ;for proper scaling of txdelay and txtail.
                        ;It is 19200 unless you have messed up the LEONID
                        ;or the SCI itself or if the new LEONID version
                        ;has changed the data/timer rate.

;was 10.5 for IC821 1200 baud tx mode
TxAttenuate   equ 4.5  ;[dB] attenuate the output CODEC's signal.
                                ;Set this between 0.0 and 90.0
                                ;to accomodate the output level for the
                                ;modulator input of your tranceiver.
                                ;Expect some noise out of the CODEC for high
                                ;attenuation levels thus you better use
                                ;an external resistor divider to achieve
                                ;high attenuation factors.
TxSymbAmpl    equ 0.250         ;Tx symbol amplitude
                                ;I experimentally set this as high as possible
                                ;before saturation in the output filters
                                ;occur. This parameter is of concern if you
                                ;really need full swing output out of the
                                ;DSP card to provide enough voltage to drive
                                ;directly the FM modulator.
                                ;Otherwise just set it to 0.25.
                                ;Make it twice as big for HilbertDecimate=2

RxCarrier  equ 1400.0   ;[Hz] receiver (expected) carrier frequency
                        ;for satellite downlink put this in the middle
                        ;of your receiver's passband
			;Originally 1600, but 1400 is better for my IC821

RxCarTrack equ 1        ;enable/disable carrier tracking.
                        ;The demodulator is made such that a mis-tune of up
                        ;to at least 50 Hz is tolerable without the need to
                        ;correct the RxCarrier frequency.
                        ;Enable RxCarTrack if you want the demodulator
                        ;to internally track the RxCarrier and
                        ;optionally to steer the receiver via the UP/DOWN
                        ;keys to correct the frequency error at the receiver
                        ;or the Doppler shift resulting from satellite's motion.
RxCarLow equ RxCarrier-200.0   ;[Hz] lower and upper limits where the carrier is allowed
RxCarUpp equ RxCarrier+200.0   ;[Hz] to move due to internal tracking
RxCarTrackWeight equ 1.0/350.0  ;carrier tracking weight per symbol
                        ;Lower numbers make the tracking
                        ;slower but more resistant to noise.
                        ;Higher numbers speed up the tracking
                        ;but make overshoots and the stability gets worse
                        ;in high noise.

RxAutoScan equ 1        ;When you enable RxAutoScan and no carrier is beeing
                        ;detected the demodulator sweeps its carrier frequency
                        ;within the range defined below.
RxScanLow equ RxCarrier-200.0 ;As soon as DCD drops the demodulator sweeps the
RxScanUpp equ RxCarrier+200.0 ;carrier within these limits until the DCD is on again
                        ;Note that searching takes time thus
                        ;wider range => more time to lock onto a signal.
                        ;In simplex mode a wide scan with short TxDelay may
                        ;result in packets which are lost because the receiver
                        ;wasn't fast enough to equire the carrier.
RxScanStep equ   1.5    ;[Hz] scanning step per symbol for auto-scan.
                        ;Larger step => faster search but may overlook
                        ;some signals and make overshoots when locking
                        ;on signals. To observe the carrier catch-up set
                        ;SPY=1, SPY_RxCarFreq=1, set up a loopback test
                        ;and watch the demodulator's frequency with SPY.EXE
                        ;while applying and removing the loopback.

RxCtrlUpDown    equ 1   ;Control the up/down switches to have the receiver
                        ;follow automatically the frequency drift.
                        ;This feature works only when RxCarTrack=1
UpDownPulseLen   equ 50 ;[symbols] Pulse length for the Up/Down corrections
                        ;50/1200 => the key is pushed for 1/24th second
UpDownPulseSepar equ 300 ;[symbols] Minimal separation between pulses
                         ;100/1200 = 1/12th second. If your receiver has
                         ;10 Hz tuning step the maximum tracking speed
                         ;is 10*12 = 120 Hz/sec which should be fine
                         ;for 70 cm downlinks of low orbit satellites.
                         ;If your tuning steps are larger you should
                         ;increase the pulse separation otherwise the modem
                         ;may overcontrol the receiver.
UpDownCorrThres equ 50.0 ;[Hz] Up/Down corrections will be done only
                         ;if frequency error is larger than this.
UpDownReverse   equ 0    ;reverse the sense: usefull for LSB operation
                         ;or if UP/DOWN lines have been wired the other way...

RxGain          equ 9.0        ;[dB] Rx CODEC's input gain
                                ;set this between 0 and 22.5 dB depending on
                                ;the audio level from your tranceiver
RxAGC           equ 0           ;enable the automatic gain adjustment
                                ;for the CODEC's audio input.
                                ;ideally you should avoid the AGC and set
                                ;RxGain for best audio level match.
                                ;Note, that the AGC may get fooled up by
                                ;for example long periods of total silence,
                                ;thus you should rather keep the quelch (if any)
                                ;open all the time.
RxAGC_MSfollow  equ 1.0/256.0   ;update weight per sample
                                ;for the audio Mean Square tracking
RxAGChold       equ 6000        ;AGC peak hold time [symbols]
RxAGCfall       equ 600         ;AGC falling period/1.5 dB step [symbols]
RxAGCminMS      equ 0.01        ;min. and max. tolerable audio Mean Square level
RxAGCmaxMS      equ 0.04

RxCorrelFollow  equ 4           ;follow factor for averaging the correlations
                                ;the actuall weight is 1/2^RxCorrelFollow
                                ;per sample.
                                ;Lower number => the demodulator will better
                                ;follow frequency instabilities (phase noise)
                                ;of the receiver and the satellite but Doppler
                                ;tracking and DCD will fluctuate more due
                                ;to noise.
                                ;When you change RxCorrelFollow
                                ;you should scale accordingly RxCarTrackWeight
                                ;and RxScanStep for optimal perfomance.

RxProcPipeLen   equ 1<<RxCorrelFollow ;the samples are stored in this pipe
                                ;and the actuall decoding is delayed
                                ;to give time for the correlations to
                                ;to become stable.

RxUseSpectralAsymmetry  equ 0   ;1 => Use spectral asymetry as part of DCD
				;criterion.

RxDCDsensitive  equ 0           ;0 => "normal" sensitivity
                                ;1 => more sensitivity (less restrictive
                                ;conditions for the DCD to go on)
RxDCDdelay      equ 16          ;wait 16 symbols before declaring DCD ON
                                ;to make sure that this is not a momentary
                                ;noise fluctuation
RxDCDhold       equ 256         ;hold DCD for 256 symbols
                                ;even if there are no signs of carrier

TxRightChannel    equ 0           ;Use the right CODEC's channel not the left.
PTTRightChannel    equ 0          ;Use the right PTT line not the left.
RxRightChannel    equ 0           ;Use the right CODEC's channel not the left.
UpDownRightChannel    equ 1       ;Use the right CODEC's channel not the left.

ListenToRxCarrier  equ 0          ;Put out the RX carrier on the other channel.

BufLen   equ    4*AliasFilterLen ;sample buffer length
                                 ;with some safety margin


;The following are the debugging options...
;normally ALL of them should be set to 0

SPY      equ 0  ; SPY=0 => KISS mode (_all_ SPY_ sub-options must be set to 0 !)
                ; SPY=1 => KISS code excluded, selected debug code included

SPY_RxAudioInp  equ 0
SPY_RxI         equ 0
SPY_RxQ         equ 0
SPY_RxIQ        equ 0   ;I/Q after the Hilbert tranformer + symbol filter
SPY_RxPhase     equ 0   ;phase of the I/Q pair
SPY_RxPhaseDiff equ 0   ;diiferences in phase ( = frequency )
SPY_RxAudioMS   equ 0   ;power (mean square) of the incoming audio
SPY_RxCorrel    equ 0   ;signal power, auto-correl, de-tune, sync.
SPY_RxProcPipePtr equ 0
SPY_RxCrossedIQ equ 0   ;demodulator: crossed succesive I/Q vectors
SPY_RxFreqCorr  equ 0   ;demodulator: frequency-error correction vector
SPY_RxCorrIQ    equ 0   ;demodulator: vector after frequency correction
SPY_RxCarFreq   equ 0   ;Rx demodulator carrier frequency
SPY_RxSamplePtr equ 0   ;how the symbol sampling point moves around
SPY_DCD		equ 0   ; DCD score

SPY_TxSymbolI   equ 0   ;transmitted symbol I-part
SPY_TxCarI      equ 0
SPY_TxFiltOutI  equ 0
SPY_TxOutputI   equ 0   ;transmitted I after the filter and the mixer
SPY_TxAudioOut  equ 0   ;samples just before they are sent to the CODEC

TxDrift         equ 0   ;enable artificial drift of the transmitter carrier
                        ;works only when SPY=1 and is intended for debugging
                        ;the demodulator in loopback tests.
TxDriftStep     equ 0.1 ;[Hz] drift step per symbol, 0.1*1200 = 120 Hz/sec
TxDriftLow      equ 1300.0      ;lower and upper limit for the drift
TxDriftUpp      equ 2200.0

MonitorRxAGC    equ 0           ;monitor the gain changes being done by the AGC
                                ;with the UP and DOWN LEDs
                                ;This option conflicts with RxCtrlUpDown
                                ;when you use the left channel.

;*************************************************************************
;macros

      if PTTRightChannel
PTT     macro mode      ;PTT line: clr/set/chg
        b\mode #4,X:$FFE4
        endm
      else
PTT     macro mode      ;PTT line: clr/set/chg
        b\mode #0,X:$FFE4
        endm
      endif

      if UpDownRightChannel
PushUp  macro mode      ;Push UP line of the TRX
        b\mode #5,X:$FFE4
        endm
      else
PushUp  macro mode
        b\mode #7,X:$FFE4
        endm
      endif

      if UpDownRightChannel
PushDown macro mode      ;Push DOWN line of the TRX
        b\mode #6,X:$FFE4
        endm
      else
PushDown macro mode
        b\mode #8,X:$FFE4
        endm
      endif

UpLED   macro mode      ;UP line (a red LED connected)
        b\mode #1,X:$FFE4
        endm

DownLED macro mode      ;DOWN line (a red LED connected)
        b\mode #2,X:$FFE4
        endm

YellowLED macro mode    ;CAT line (a yellow LED connected)
        b\mode #3,X:$FFE4
        endm

OrangeLED macro mode    ; (an orange LED connected)
        b\mode #14,X:$FFE4
        endm

DCDLed  macro mode      ; DCD (Green) LED clr/set/chg
        b\mode #13,X:$FFE4
        endm

;*************************************************************************
;The actuall code (internal/external program RAM)

        LOMEM P:$0000
        HIMEM P:$1FFF

        org     p:user_code

        jmp <Initialize

;-----------------------------------------------------------------------------

ProcessLoop     ;main loop to process CODEC's samples

RxProcess
        move #>HilbertDecimate,a ;a = 1 (wait for one new sample)
        move X:<CodecPtr,r2
        move #BufLen*4-1,m2     ;set m2 so r2 wraps correctly
        jsr <WaitSampleBlock
      if RxRightChannel
        move (r2)+
      endif

        move #<4,n2

      if SPY_RxAudioInp
        move r2,y1
        .loop #HilbertDecimate
          move X:(r2),a
          jsr <SpyA
          move (r2)+n2
        .endl
        move y1,r2
      endif
      if RxAGC                  ;if AGC enabled
      move r2,y1
      .loop #HilbertDecimate
        move X:(r2)+n2,x0       ;update the mean signal square (MS)
        mpyr x0,x0,a X:<RxAudioMS,x1 ;of the incoming audio
        move a,x0 #RxAGC_MSfollow,y0
        mpy x0,y0,a #1.0-RxAGC_MSfollow,y0
        macr x1,y0,a
        move a,X:<RxAudioMS
      .endl
      move y1,r2
       if SPY_RxAudioMS
         jsr <SpyA
       endif
      endif

        move #4*(AliasFilterLen-HilbertDecimate),n2
        move #AliasFilterI,r4   ;r4 to address the input filter
        move #<AliasFilterLen-1,m4
        move (r2)-n2            ;move back in the CODEC input buffer
        move #<4,n2             ;n2=4 for easier input adressing
        move r2,x1              ;save r2 (where we start the filter tap)
        clr a X:(r2)+n2,x0 Y:(r4)+,y0
        .loop #AliasFilterLen-1
          mac x0,y0,a X:(r2)+n2,x0  Y:(r4)+,y0
        .endl
        macr x0,y0,a #<AliasFilterQ-AliasFilterI,n4
        move x1,r2              ;restore the tap start
        move (r4)+n4
        clr b X:(r2)+n2,x0 Y:(r4)+,y0
        .loop #AliasFilterLen-1
          mac -x0,y0,b X:(r2)+n2,x0  Y:(r4)+,y0
        .endl
        macr -x0,y0,b           ;a,b = the complex sample (I/Q)

        move ab,L:<RxTmp

        move X:<RxCarPhase,x0  ;get the receiver carrier phase
        move X:<RxCarFreq,a    ;advance the phase
        add x0,a
        move a1,X:<RxCarPhase
        jsr <IQ               ;compute I and Q (modifies a,b,x,y,r0,m0,n0)

        ; N1OWU: put Rx carrier on unused output channel for testing
        if ListenToRxCarrier
          move X:<CodecPtr,r2
          nop
          move (r2)+
          if TxRightChannel
            move a1,Y:(r2)
          else
            move (r2)+
            move a1,Y:(r2)
          endif
        endif

        move a,y1
        move L:<RxTmp,x
        mpy x1,y1,a b,y0                        ;mix with the local carrier
        macr x0,y0,a  X:<RxSymbolFilterPtr,r0   ;and pass through
        move #<SymbolShapeLen-1,m0              ;the symbol filter
        mpy x0,y1,b #RxSymbolShape,r4
        macr -x1,y0,b a,X:(r0)
        move m0,m4
        clr a b,Y:(r0)+
        clr b X:(r0),x0 Y:(r4)+,y0
        .loop #SymbolShapeLen-1
          mac x0,y0,a Y:(r0)+,x0
          mac x0,y0,b X:(r0),x0 Y:(r4)+,y0
        .endl
        macr x0,y0,a Y:(r0)+,x0
        macr x0,y0,b r0,X:<RxSymbolFilterPtr

        move ab,L:<RxTmp

      if SPY_RxProcPipePtr
        move r1,a
        rep #8
          asl a
        jsr <SpyA
      endif

      if SPY_RxI
        jsr <SpyA
      endif

      if SPY_RxQ
        tfr b,a
        jsr <SpyA
      endif

      if SPY_RxPhase
        jsr <Phase
        jsr <SpyA
      endif

      if SPY_RxPhaseDiff
        jsr <Phase
        move X:<RxPrevPhase,x0
        sub x0,a a,X:<RxPrevPhase
        move #<0,a2
        jsr <SpyA
      endif

      if SPY_RxIQ
        jsr <SpySync
        .if <cc>
          jsr <SpyA
          tfr b,a
          jsr <SpyA
        .endi
      endif

        move X:<RxProcPipePtr,r1
        move #RxProcPipeLen*SymbolSepar-1,m1
        move L:<RxTmp,ab
        move ab,L:-(r1)          ;put in the most recent I/Q
        move r1,X:<RxProcPipePtr ;and increment the pointer

        move X:<RxCorrelPtr,r4  ;load the correlation pointer
        move #<SymbolSepar*4-1,m4

        move r4,a               ;if correlation pointer is same as
        move X:<RxSamplePtr,x0  ;the sampling pointer
        eor x0,a1 #<SymbolSepar,n1
        .if <eq>                ;then sample the data bit
          move (r1)-n1
          move L:(r1)-n1,x      ;get the two vectors to be compared
          move L:(r1)+n1,y      ;from the very end of the pipe
          mpy x1,y1,a (r1)+n1
          mac x0,y0,a
          mpy x1,y0,b #<2,n4
          mac -x0,y1,b (r4)+
          jsr <RaughNormIQ
      if SPY_RxCrossedIQ
          move ab,L:<SpyTmp
      endif
          move a,x1
          move b,x0
          move L:(r4)+,a        ;apply frequency correction
          move L:(r4)-n4,b
          jsr <RaughNormIQ
      if SPY_RxFreqCorr
          move ab,L:<SpyTmp
      endif
          move a,y1
          move b,y0
          mpy x1,y1,a ab,L:<RxTmp ;correlate with the expected vector
          mac x0,y0,a            ;save freq. correction for tracking
      if SPY_RxCorrIQ
          mpy -x0,y1,b
          mac x1,y0,b
          move ab,L:<SpyTmp
      endif
      if !SPY                    ;get out the data bit
          neg a
          btst #7,a2
          putbit
      endif
      if SPY_RxCrossedIQ||SPY_RxFreqCorr||SPY_RxCorrIQ
          move L:<SpyTmp,ab
          jsr <SpySync
          .if <cc>
            jsr <SpyA
            tfr b,a
            jsr <SpyA
          .endi
      endif
                                ;DCD decision
          move #<SymbolSepar*4/2,n4
          move L:(r4)+,b        ;b = power
          asr b L:(r4+n4),x     ;x = autocorrelation opposite the symbol
          move L:(r4)+,a        ;a = autocorrelation at the symbol
          sub x,a (r4)+         ;a = autocorrelation difference
        if RxDCDsensitive
          asr  b		;N1OWU: This "asr b" used to be always done,
          subl b,a		;but it made the DCD too sensitive...
        else
          sub b,a
        endif
          move a1,x0

	  if RxUseSpectralAsymmetry
            move L:(r4)-,a        ;a = spectral asymetry
            rep #3                ;a = 2 * SymbolSepar * spectral asymetry
              asl a               ;here the multiplication is hardwired
            asl a L:(r4)-,y       ;y = mistune
            sub y,a
            abs a (r4)-
            sub a,b
          else
            move  (r4)-
            move  (r4)-
            move  (r4)-
            clr b
          endif
          if SPY_DCD
            move x0,a
            rep #7
              asl a
            jsr <SpyA
          endif
          or x0,b1 X:<RxDCDcounter,a
          btst #23,b1

          .if <cc>              ;if DCD condition is satisfied
            bset #1,X:<RxState
            move #1.0/RxDCDdelay,x1 ;increment the counter
            add x1,a #0.5,x0
            btst #0,X:<RxState
            move a,X:<RxDCDcounter
            .if <cc>            ;if DCD not yet declared ON
              cmp x0,a          ;check if counter above 1/2
              .if <gt>          ;if so then
                bset #0,X:<RxState      ;declare the DCD as ON
                if !SPY
                  caron
                endif
                DCDLed set              ;turn on the DCD LED
              .endi
            .endi
          .else                 ;if DCD condition not satisfied
            bclr #1,X:<RxState
            move #1.0/RxDCDhold,x1
            sub x1,a #-0.5,x0   ;decrement the counter
            btst #0,X:<RxState
            move a,X:<RxDCDcounter
            .if <cs>            ;if DCD is declared ON
              cmp x0,a          ;check if counter below -1/2
              .if <lt>          ;if so then
                bclr #0,X:<RxState      ;declare the DCD as OFF
                if !SPY
                  caroff
                endif
                DCDLed clr              ;turn off the DCD LED
              .endi
            .endi
          .endi

        if RxCarTrack
          btst #1,X:<RxState      ;DCD condition satisfied ?
          .if <cs>                ;if so then track the carrier
            move L:<RxTmp,ab
            jsr <Phase            ;track the freq. of the incoming signal
            move a,x0 #RxCarTrackWeight,y0
            move X:<RxCarFreq,a
            macr x0,y0,a #2.0*HilbertDecimate*RxCarLow/SampleFreq,x0
            cmp x0,a #2.0*HilbertDecimate*RxCarUpp/SampleFreq,x1  ;keep the carrier
            tlt x0,a                              ;frequency within given limits
            cmp x1,a
            tgt x1,a
            move a,X:<RxCarFreq
          .else                 ;otherwise
           btst #0,X:<RxState   ;if DCD is declared OFF then
           .if <cc>
         if RxAutoScan          ;sweep the carrier within the defined region
            move X:<RxScanStepFreq,b
            tst b X:<RxCarFreq,a
            .if <pl>
              add b,a #2.0*HilbertDecimate*RxScanUpp/SampleFreq,x0
              cmp x0,a
              .if <ge>
                neg b x0,a
                move b,X:<RxScanStepFreq
              .endi
            .else
              add b,a #2.0*HilbertDecimate*RxScanLow/SampleFreq,x0
              cmp x0,a
              .if <le>
                neg b x0,a
                move b,X:<RxScanStepFreq
              .endi
            .endi
            move a,X:<RxCarFreq
         else                           ;when RxAutoScan==0
           move X:<RxCarFreq,a          ;move the carrier towards nominal
           move #2.0*HilbertDecimate*RxCarrier/SampleFreq,x1
           sub x1,a #2.0*HilbertDecimate*RxScanStep/SampleFreq,y0
           .if <pl>
              sub y0,a
              .if <lt>
                clr a
              .endi
           .else
              add y0,a
              .if <gt>
                clr a
              .endi
           .endi
           add x1,a
           move a,X:<RxCarFreq
         endif  ;of RxAutoScan
           .endi
          .endi
        endif   ;of RxCarTrack

      if SPY_RxCarFreq
          move X:<RxCarFreq,a
          move #2.0*HilbertDecimate*RxCarrier/SampleFreq,x0
          sub x0,a
          jsr <SpyA
      endif
      if RxCtrlUpDown
        move X:<RxUpDownPulse,a
        tst a #>1,x0
        .if <ne>                        ;if pulse timer running
          sub x0,a                      ;decrement the timer
          move a,X:<RxUpDownPulse
          .if <eq>                      ;if it reached zero
            PushUp clr                  ;turn off both UP and DOWN
            PushDown clr
	    if !MonitorRxAGC
              UpLED clr
              DownLED clr
            endif
          .endi
        .endi
        move X:<RxUpDownHold,a
        tst a #>1,x0
        .if <ne>                        ;if hold timer running
          sub x0,a                      ;decrement it
          move a,X:<RxUpDownHold
        .endi
      endif

    if RxCtrlUpDown
      ;btst #1,X:<RxState
      btst #0,X:<RxState  ;N1OWU: Only ctrlUpDown if DCD light is on
      move X:<RxUpDownHold,a
      .if <cs>                          ;if DCD is ON
        tst a X:<RxCarFreq,a
        .if <eq>                        ;if hold timer not running
          move #2.0*HilbertDecimate*RxCarrier/SampleFreq,x0
          sub x0,a #2.0*HilbertDecimate*UpDownCorrThres/SampleFreq,x0
          cmpm x0,a
          .if <gt>                      ;if frequency offset larger than desired
            tst a #>UpDownPulseLen,x0
            move x0,X:<RxUpDownPulse    ;push either UP or DOWN bottoms
          if UpDownReverse
            .if <mi>
          else
            .if <pl>
          endif
              PushUp set
	      if !MonitorRxAGC
                UpLED set
              endif
            .else
              PushDown set
	      if !MonitorRxAGC
                DownLED set
              endif
            .endi
            move #>UpDownPulseSepar,x0
            move x0,X:<RxUpDownHold
          .endi
        .endi
      .endi
    endif

        .endi   ;if we are at the symbol sampling moment

        move #<SymbolSepar*4/2,n4
        move X:<RxSamplePtr,x0
      if SPY_RxSamplePtr
        tfr x0,a #>RxCorrel,y0
        sub y0,a
        rep #8
          asl a
        jsr <SpyA
      endif
        lua (r4)+n4,r5
        move m4,m5
        move r5,a        ;if sampling pointer is oposite the correlation
        eor x0,a1 #<4,n4 ;pointer then adjust the sampling point
        .if <eq>
          move n4,n5
          move (r4)+
          move (r5)+
          move L:(r4)-n4,y
          move L:(r5)-n5,a
          sub y,a L:(r4)+n4,y
          move L:(r5)+n5,b
          sub y,b
          cmp a,b
          .if <gt>
            move (r5)-n5
            move (r5)-
            move r5,X:<RxSamplePtr
          .else
            move L:(r4+n4),y
            move L:(r5+n5),b
            sub y,b
            cmp a,b
            .if <gt>
              move (r5)+n5
              move (r5)-
              move r5,X:<RxSamplePtr
            .endi
          .endi
          move (r4)-
        .endi

        move #<SymbolSepar/2,n1         ;update correlations which are used
        move #<RxCorrel0-RxCorrel,n4    ;for freq. and timing corrections
                                        ;and DCD decisions
        move X:(r1),x0                  ;signal power
        mpy x0,x0,a Y:(r1),x0
        mac x0,x0,a
        jsr <LowPass2el

        move L:(r1)+n1,y                ;autocorrelation at SymbolSepar
        move L:(r1+n1),x
        mpy x1,y1,a (r1)-n1
        mac x0,y0,a
        move a,L:<RxTmp
        .if <mi>
          neg a
        .endi
        jsr <LowPass2el

        move L:(r1)+n1,y
        move L:(r1+n1),x
        mpy x1,y0,a (r1)-n1     ;frequency error
        mac -x0,y1,a
        btst #23,X:<RxTmp
        .if <cs>
          neg a
        .endi
        jsr <LowPass2el

                                ;spectral asymetry
        move L:(r1)+,y
        move L:(r1)-,x
        mpy x1,y0,a
        mac -x0,y1,a
        jsr <LowPass2el

        move r4,X:<RxCorrelPtr

        if SPY_RxCorrel
          move X:<RxProcPipePtr,a1
          move #>RxProcPipe,x0
          eor x0,a1 #RxCorrel,r4
          .if <eq>
            jsr <SpySync
            .if <cc>
              move #<4,n4
              move X:<RxCarFreq,a
              move #2.0*HilbertDecimate*RxCarrier/SampleFreq,x0
              sub x0,a
              jsr <SpyA
              .loop #4
                .loop #SymbolSepar
                  move L:(r4),a
                  rep #4
                    asl a
                  jsr <SpyA
                  move (r4)+n4
                .endl
                clr a
                jsr <SpyA
                move (r4)+
              .endl
              jsr <SpyA
            .endi
          .endi
        endif

;Here comes the transmitter part

	if UseNewTransmitter
	  jsr NewTransmitter
	else
	  jsr OriginalTransmitter
	endif

; N1OWU:  Sample the SCI Tx data line and turn on/off the yellow LED
; accordingly, to show when data is being sent out the serial port.
; (This ought to be done in Leonid...)

	if !RxAGC   ; Rx AGC also uses the yellow LED...
	  btst	#1,x:$ffe5   ; Port C data register
	  .if <cs>
	    bclr	#3,x:$ffe4
	  .else
	    bset  #3,x:$ffe4
	  .endi
	endif

; Do the same for the Rx data line and the orange LED.

	btst	#0,x:$ffe5   ; Port C data register
	.if <cs>
	  OrangeLED clr
	.else
	  OrangeLED set
	.endi


        jmp     <ProcessLoop

;-----------------------------------------------------------------------------

	if UseNewTransmitter

NewTransmitter

; N1OWU: This generates Manchester-coded output is
; a straightforward way.  It works better for satellites than the
; original transmitter code.  A Manchester-encoded square wave is
; generated, and passed through the TX anti-alias filter.
; Note: The "I/Q" business is unnecessary.  Th I value is
; the only meaningful one.

;we begin with getting the digital data to be sent out
;and generate the I/Q values for this data

        move X:<TxBitTime,b     ;decrement the bit time counter
        move #>1,x1
        sub x1,b                ;did it come to a moment to put out a new bit ?
        .if <eq>              
	  if RxAGC
	      ; This is being done here because we want to do it
	      ; once per bit, not per sample.
	      jsr <RxCheckAudioMS
	  endif

          move #>SymbolSepar,b  ;if so, restart the counter 

	  if SPY
	    jsr <Rand48         ; in SPY mode, use a pseudo-random bit
	    btst #10,a1
	  else
	    getbit
	  endif

          move X:<TxDigLevel,a
          .if <cs>               ;negate the signal level for one-bits. Thus
            neg a		 ;for zero-bits the phase is inverted.
            move a,X:<TxDigLevel
          .endi
        .else                  
          move X:<TxDigLevel,a
          move #>SymbolSepar/2,x1
          cmp x1,b               ;see if we are at mid-bit
          .if <eq>
            neg a                ;if at mid-bit, invert the signal level
            move a,X:<TxDigLevel
	  .endi
	.endi
        move b,X:<TxBitTime

        if SPY_TxSymbolI
          jsr <SpyA
        endif

	if !TxContCarrier
	  PTT tst                ;but if PTT not active
	  .if <cc>               ;then set I=0 as well
	    clr a
	  .endi
	endif

        ; Simply stick the sample in the output buffer...
        ; The CODEC output filtering (~4.2 kHz cutoff) should be sufficient.
        ; If HilbertDecimate is > 1, stick it in more than once...

        move X:<CodecPtr,r2
        move #BufLen*4-1,m2
        move #<4,n2

        move (r2)+
	if TxRightChannel
	  move (r2)+
	endif

      .loop #HilbertDecimate
	move a,Y:(r2)+n2
	if SPY_TxAudioOut
	    jsr <SpyA
	    nop
	endif
      .endl

      move (r2)-
      if TxRightChannel
        move (r2)-
      endif

      ; R2 has been advanced #HilbertDecimate steps.
      move r2,X:<CodecPtr

      rts

      endif

;-----------------------------------------------------------------------------

	if !UseNewTransmitter

OriginalTransmitter

; N1OWU: This is the original transmitter code, which can
; generate either PSK or pseudo-Manchester.  It works rather poorly
; for satellites...

;we begin with getting the digital data to be sent out
;and generate the I/Q values for this data
        move X:<TxBitTime,a     ;decrement the bit time counter
        move #>1,x1
        sub x1,a #<0,b          ;did it come to a moment to put out a new bit ?
        .if <ne>                ;if not yet, then we put out a zero
          clr a a,X:<TxBitTime
        .else                   ;otherwise we restart the counter
          move #>SymbolSepar,a  ;and put out the new bit
          move a,X:<TxBitTime
      if TxCarSync              ;if requested we should synchronize the carrier
          move #0.5,a           ;a bit tricky: what should the TxCarPhase be
          move a,X:<TxCarPhase  ;at this moment ?
      endif

      if RxAGC
	  ; This is being done here because we want to do it
	  ; once per bit, not per sample.
	  jsr <RxCheckAudioMS
      endif

      if SPY                    ;in SPY mode send continues stream of random data
          if TxDrift                    ;Drift the Tx carrier to simulate
            move X:<TxDriftStepFreq,b   ;Doppler drift
            tst b X:<TxCarFreq,a
            .if <pl>
              add b,a #2.0*HilbertDecimate*TxDriftUpp/SampleFreq,x0
              cmp x0,a
              .if <ge>
                neg b x0,a
                move b,X:<TxDriftStepFreq
              .endi
            .else
              add b,a #2.0*HilbertDecimate*TxDriftLow/SampleFreq,x0
              cmp x0,a
              .if <le>
                neg b x0,a
                move b,X:<TxDriftStepFreq
              .endi
            .endi
            move a,X:<TxCarFreq
          endif
          jsr <Rand48
          btst #10,a1
          .if <cs>
            clr b #TxSymbAmpl,a
          .else
            clr b #-TxSymbAmpl,a
          .endi
       else                     ;in KISS-TNC mode get data from LEONID
          getbit
          move X:<TxDigLevel,a
          .if <cc>               ;invert the phase for zero-bits
            neg a
          .endi
          clr b a,X:<TxDigLevel  ;set Q=0 (we only do BPSK)
        if !TxContCarrier
          PTT tst                ;but if PTT not active
          .if <cc>               ;then set I=0 as well
            clr a
          .endi
        endif
       endif
        .endi

        if SPY_TxSymbolI
          jsr <SpyA
        endif

        move X:<TxSymbolFilterPtr,r0
        move #<SymbolShapeLen-1,m0
        move #TxSymbolShape,r4
        move m0,m4
        move a,X:(r0)
        clr a b,Y:(r0)+
        clr b X:(r0),x0 Y:(r4)+,y0
        .loop #SymbolShapeLen-1
          mac x0,y0,a Y:(r0)+,x0
          mac x0,y0,b X:(r0),x0 Y:(r4)+,y0
        .endl
        macr x0,y0,a Y:(r0)+,x0
        macr x0,y0,b r0,X:<TxSymbolFilterPtr

        move ab,L:<RxTmp
        if SPY_TxFiltOutI
          jsr <SpyA
        endif

        move X:<TxCarPhase,x0  ;get the transmitter carrier phase
        move X:<TxCarFreq,a    ;advance the phase
        add x0,a
        move a1,X:<TxCarPhase
        jsr <IQ               ;compute I and Q (modifies a,b,x,y,r0,m0,n0)

        if SPY_TxCarI
          jsr <SpyA
        endif
        move a,y1
        move L:<RxTmp,x
        mpy x1,y1,a b,y0      ;mix with the carrier
        macr -x0,y0,a
        mpy x0,y1,b
        macr x1,y0,b

        if SPY_TxOutputI
          jsr <SpyA
        endif

TxOutput                        ;a/b = I/Q to be send out
        move X:<CodecPtr,r2
        move #BufLen*4-1,m2

        move #AliasFilterI,r4
        move #<AliasFilterLen-1,m4
        move #<AliasFilterQ-AliasFilterI,n4
        move (r2)+
      if TxRightChannel
        move (r2)+
      endif
        move #4*(AliasFilterLen-HilbertDecimate),n2
        move a,y1
        move b,y0
        move (r2)-n2            ;move back in the CODEC input buffer
        move #<4,n2             ;n2=4 for easier input adressing
      if SPY_TxAudioOut
        .loop #HilbertDecimate
          move (r2)-n2
        .endl
        .loop #HilbertDecimate
          move Y:(r2)+n2,a
          jsr <SpyA
          nop
        .endl
      endif
        .loop #AliasFilterLen-HilbertDecimate
          move Y:(r4),x0
          move Y:(r2),a
          mac x0,y1,a Y:(r4+n4),x0
          macr -x0,y0,a (r4)+
          move a,Y:(r2)+n2
        .endl
        .loop #HilbertDecimate
          move Y:(r4),x0
          mpy x0,y1,a Y:(r4+n4),x0
          macr -x0,y0,a (r4)+
          move a,Y:(r2)+n2
        .endl
        move (r2)-
      if TxRightChannel
        move (r2)-
      endif
        move r2,X:<CodecPtr

      rts

      endif

;-----------------------------------------------------------------------------

CheckSampleBlock        ;a = how many samples we want
        asl a           ;r2 = where we wait for the block to start
        asl a #>2,x1
        add x1,a
        move a,x1
CheckSampleBlock_1
        move r7,a
        move r2,x0
        sub x0,a
        jpl <CheckSample_cmp
          move #>BufLen*4,x0
          add x0,a
CheckSample_cmp
        cmp x1,a
        rts             ;on output: a,x0,x1 are modified
                        ;If carry=0 => we are sure that:
                        ;1. there are at least A new input samples at X:(r2)
                        ;   to be read
                        ;2. there are at least A locations at Y:(r2) to
                        ;   be written


WaitSampleBlock         ;wait for a block of samples from the CODEC
                        ;on input: a = how many samples we want
                        ;          r2 = where the block starts
        jsr <CheckSampleBlock
        jcc <WaitSample_ret
WaitSample_loop
        wait            ;wait (at low power) until an interrupt comes
        jsr <CheckSampleBlock_1
        jcs <WaitSample_loop
WaitSample_ret
        rts             ;on output: a,x0,x1 are modified
                        ;We are sure that:
                        ;1. there are at least A new input samples at X:(r2)
                        ;   to be read
                        ;2. there are at least A locations at Y:(r2) to
                        ;   be written


;-----------------------------------------------------------------------------

        if RxAGC

RxCheckAudioMS                  ;check the audio level and adjust
                                ;the CODEC's input gain if needed
      if MonitorRxAGC
        UpLED clr
        DownLED clr
      endif

        move X:<RxAudioMS,a
	move X:<CodecPtr,r2
        move #BufLen*4-1,m2     ;r2/m2 must point to the CODEC's buffer
        move #RxAGCminMS,x0     ;see, if the MS is below the minimal level
        cmp x0,a X:<CodecPtr,r2
        jcc <CheckMSmax         ;if not, then check if above the max. level
                                ;if so, increase the CODEC's input gain
        move X:<RxAGCcount,a    ;decrement the timeout
        move #>1,x0
        sub x0,a 
        move a,X:<RxAGCcount
        jgt <CheckMS_OK         ;leave if not yet zero
        jsr <RxGainUp           ;increase the CODEC's gain
        jcs <CheckMS_OK
        move #0.7071,y0         ;increase the MS to follow
        move X:<RxAudioMS,x0    ;the gain change faster
        mpyr x0,y0,a #>RxAGCfall,x0 ;preset the AGC "timer" to prohibit
        asl a x0,X:<RxAGCcount      ;gain increase for the RxAGCfall period
        move a,X:<RxAudioMS
      if MonitorRxAGC
        UpLED set
      endif
        jmp <CheckMS_OK

CheckMSmax                      ;is the MS above the given maximum
        move #>RxAGChold,y0     ;preset the AGC "timer" to prohibit gain increase
        move #RxAGCmaxMS,x0     ;for the RxAGChold period
        cmp x0,a y0,X:<RxAGCcount ;MS above maximum ?
        jcs <CheckMS_OK         ;if not then we are done
GainDown
        jsr <RxGainDown         ;otherwise decrease the CODEC's gain
        jcs <CheckMS_OK
        move #0.7071,y0         ;decrease the MS to follow expected
        move X:<RxAudioMS,x0    ;gain reduction faster
        mpyr x0,y0,a #>RxAGChold,x0     ;initialize the AGC hold count-down
        move a,X:<RxAudioMS
        move x0,X:<RxAGCcount
      if MonitorRxAGC
        DownLED set
      endif

CheckMS_OK
        rts

RxGainUp                        ;increase the CODEC's input gain (both channels)
        clr a #>$0F0F00,x0      ;r2/m2 should point to the CODEC's buffer
        move Y:(r2),a1          ;get the CODEC's input control word
        and x0,a                ;extract the gain bits
	cmp x0,a  #>$010100,x0  ;already maximum ?
        jeq <RxGainChange_abort ;if so then abort this attempt
	YellowLED clr
	add x0,a  #>$F0F000,x0  ;if not, increment the gain by 1
	move a1,x1
	move Y:(r2),a1          ;and reload all the control words
	and x0,a  n2,x0         ;in the output buffer
	or x1,a  #<4,n2         ;make n2=4 for a moment
	.loop #BufLen
	  move a1,Y:(r2)+n2
	.endl
	move x0,n2              ;restore n2
        andi #$FE,ccr           ;clear carry
        rts                     ;modifies a,x0,x1

RxGainDown                      ;decrease the CODEC's input gain (both channels)
        clr a #>$0F0F00,x0      ;r2/m2 should point to the CODEC's buffer
        move Y:(r2),a1          ;get the CODEC's input control word
        and x0,a  #>$010100,x0  ;extract the gain bits
        sub x0,a  #>$F0F000,x0  ;attempt to decrease the gain
        jcs <RxGainChange_abort ;jump if overflow
	YellowLED clr
        move a1,x1
        move Y:(r2),a1          ;reload all the input control words
        and x0,a  n2,x0         ;in the buffer with the new input gain
        or x1,a  #<4,n2         ;n2=4 for a moment
        .loop #BufLen
          move a1,Y:(r2)+n2
        .endl
        move x0,n2              ;restore n2
        andi #$FE,ccr           ;clear carry
        rts                     ;modifies a,x0,x1

RxGainChange_abort
	YellowLED set
        ori #$01,ccr            ;set carry to indicate that we couldn't
        rts                     ;change the gain

        endif

;-----------------------------------------------------------------------------

LowPass2el ;a = input, r4,r4+n4 = filter storage (2 elements)
        move L:(r4+n4),b  ;b = E
        move L:(r4),x   ;x = F
        add x,b         ;b = E+F
        asr b           ;b = (E+F)/2
        sub b,a L:(r4+n4),b ;a = I - (E+F)/2, b = E
        rep #RxCorrelFollow-1
          asr a         ;a = (I - (E+F)/2)/16
        add a,b L:(r4+n4),a ;b = new E, a = old E
        sub x,a b,L:(r4+n4) ;save new E, a = E - F
        rep #RxCorrelFollow
          asr a
        add x,a         ;a = F + (E-F)/32
        move a,L:(r4)+  ;save new F
                        ;a = output, r4 moved by 1, x,b modified
        rts             ;

;-----------------------------------------------------------------------------

PI      equ     3.14159265358979323846

;this routine computes a cosine/sine pair using the sine ROM
;with a second order (linear+quadrature) approximation between table points
IQ                              ;x0 = angle ( -1 = -PI, +1 = +PI)
        move #>$80,x1   ;shift out 8 most significant bits
        mpy x0,x1,a  #>$FF,x0
        move x0,m0
        and x0,a     #>$100,x0
        or x0,a      #<$40,n0
        move a1,r0      ;put the 8 most significant bits into r0 with offset = $100
        move a0,y0      ;save the remaining bits in y0
        jclr #23,y0,SinTable_lev2
          move (r0)+
SinTable_lev2
        move Y:(r0+n0),x0       ;x0 = coarse cosine
        move Y:(r0),x1          ;x1 = coarse sine
        mpyr x1,y0,a  #PI/256.0,y1
        tfr x0,a  a,x1
        macr -x1,y1,a           ;a = fine cosine
        mpyr x0,y0,b  Y:(r0),x1
        tfr x1,b  b,x1
        macr x1,y1,b  #PI*PI/2.0/65536.0,y1  ;b = fine sine
        mpyr y0,y0,a  a,x0
        move a,y0
        mpyr y0,y1,a
        tfr x0,a  a,y1
        macr -x0,y1,a  b,x1     ;a = super fine cosine
        macr -x1,y1,b           ;b = super fine sine
        rts                     ;x,y are modified
                                ;r0,m0,n0 are modified
                                ;maximum error is about 0.7E-6
                                ;execution time 4+64+4 clock cycles
                                ;including "jsr <IQ_lev2" and "rts"

RaughNormIQ             ;scale an I/Q pair such that the larger number
                        ;is within the range +/-0.5..1.0
        cmpm a,b        ;which number is greater ?
        jge <NormAftB   ;if b then follow the normalization according to it
        tst a           ;otherwise follow a
        jes <RightA     ;if too big (extension in use) the shift to the right
        jnr <DoneA      ;if normalized then we are done
LeftA     asl b         ;otherwise shift left both numbers
	  asl a
        jnn <LeftA      ;until A is normalized
        rts
RightA    asr b         ;if extension set then shift both number to the right
	  asr a
        jes <RightA     ;until a is normalized
DoneA   rts

NormAftB                ;same stuff for b
	tst b
	jes <RightB
        jnr <DoneB
LeftB	  asl a
	  asl b
	jnn <LeftB
        rts
RightB	  asr a
	  asr b
	jes <RightB
DoneB   rts


;The following routine computes the phase of the given I/Q pair
;this routine is not very precise: maximum error is 2E-4 (2E-4/PI radians)
;the error could be made lower but I required that the calculation
;is exact for PI/4, PI/2, PI*3/4, etc.
Phase      ;a = I-part (24 bits), b = Q-part (24 bits)
           ;-1.0 <= a < 1.0, -1.0 <= b < 1.0
        tst b #<0,x0    ;if Q-part negative => turn by PI
        .if <mi>
          neg a #<%10000000,x0
          neg b
        .endi
        tst a           ;if I-part negative => turn by -PI/2
        .if <mi>
          bset #22,x0
          tfr a,b b,a   ;swap I with Q
          neg b         ;negate the new Q
        .endi
        add b,a                 ;add Q to I (what if I+Q >= 1.0 ?)
        asr a                   ;I+Q /= 2 in case it is > 1
        asr b a,x1              ;Q /= 2 because of the above, x1 = I+Q
        andi #$FE,ccr           ;divive Q by (I+Q)
        rep #24
          div x1,b
        tfr x0,a b0,x0          ;x0 = Q div (I+Q), a=approx angle
        tfr x0,b #0.5,x0        ;b = Q/(I+Q)
        sub x0,b #0.25,x0       ;b = Q/(I+Q) - 1/2 =: X
        add x0,a b,x1 #0.63361654,y1 ;angle += 0.25, x1 = X
        mac x1,y1,a x1,x0       ;angle += F1*X, x0 = X
        mpyr x0,x0,b #-0.73728217,y1 ;b = X*X
        mpyr x1,y1,b b,x0       ;b = F3*X, x0 = X*X
        move b,y1
        mac x0,y1,a x0,y0       ;angle += F3*X * X*X, y0 = X*X
        mpyr x0,x0,b #0.85568791,y1 ;b = (X*X)*(X*X)
        mpyr x1,y1,b b,x0       ;b = F5*X, x0 = X*X*X*X
        move b,y1
        mac x0,y1,a             ;angle += F5*X * X*X*X*X
        mpyr x0,y0,b #-0.17769569,y1 ;b = (X*X*X*X)*(X*X)
        mpyr x1,y1,b b,x0       ;b = F5*X, x0 = X*X*X*X*X*X
        move b,y0
        macr x0,y0,a            ;angle += F5*X * X*X*X*X*X*X
        rts             ;a = the phase (-1.0 => -PI, 1.0 => PI)
                        ;b,x,y are modified
                        ;execution time is about 130 cycles (65 instructions)


;-----------------------------------------------------------------------------

Rand48                          ;a simple random number generator
        move L:<Rand,a
        asl a L:<RandInc,x
        adc x,a
        move a10,L:<Rand
        rts                     ;returns a pseudo-random number in a
                                ;modifies x

;-----------------------------------------------------------------------------

        if !SPY

; KISS control frame handling - called by LEONID when a KISS control-type
; frame is received with non-standard parameters
KISSctrl        ;a = parameter number, x1 = parameter value
                ;we should only use a,x0,x1,r3
        rts

; transmitter PTT control - a routine called by LEONID to say that we
; should transmit (carry=1) or stop transmitting (carry=0)
PTTctrl jcc PTT_off
        PTT set
        rts
PTT_off PTT clr
        rts

        endif

;-----------------------------------------------------------------------------

Initialize      ;initialize registers, buffers, windows, etc.

        ori #%00000100,omr      ;enable the sine ROM table
                                ;this is for the IQ routine

      if !SPY
        ;;move #>@cvi(TxDelay*SciTimerRate/100.0),a ;set TxDelay
        ;;move #>@cvi(TxTail*SciTimerRate/100.0),a ;set TxTail

	move	x:<txdelay,x0	    ; set kiss-txdelay from txdelay
	move    #>(baud/100),x1	    ; time scale parameter
	mpy	x0,x1,a
	asr	a		    ; integer multiply correction
	move    a0,p:kiss_pars      ; product in low order word

	move	x:<txtail,x0	    ; set kiss-txtail from txtail
	move    #>(baud/100),x1	    ; time scale parameter
	mpy	x0,x1,a
	asr	a
	move    a0,p:kiss_pars+3
        
        move x:<txduplex,a1         ;set kiss-txduplex
        move a1,p:kiss_pars+4       ;boolean parameter; no scaling needed
      endif

        clr a #RxProcPipe,r0
        move #RxProcPipeLen*SymbolSepar-1,m0
        rep #RxProcPipeLen*SymbolSepar
          move a,L:(r0)+

        clr a #RxSymbolFilterTap,r0
        move #SymbolShapeLen-1,m0
        rep #SymbolShapeLen
          move a,L:(r0)+
        clr a #TxSymbolFilterTap,r0
        rep #SymbolShapeLen
          move a,L:(r0)+

        clr a #RxCorrel,r0
        move #SymbolSepar*4-1,m0
        rep #SymbolSepar*4
          move a,L:(r0)+
        clr a #RxCorrel0,r0
        rep #SymbolSepar*4
          move a,L:(r0)+

      if !SPY
        move #KISSctrl,a1       ;switch serial interface to KISS mode
        move #PTTctrl,b1
        opensc
      endif

; N1OWU:  Activate the relay that switches the analog I/O
; to the 9600 baud FSK port.  It's connected to bit 9 of port B.
; This is correct for the IC-821 setup.  It causes the radio
; to get line-level, not mike-level output.

;;not any more	bset	#9,x:$ffe4 

        move #Buffer+2,r7        ;for the CODEC's interrupt routine
        move #BufLen*4-1,m7

        move #Buffer,r2          ;for "cdctrl" to initialize the buffer
        move #<4-1,n2
        move #BufLen*4-1,m2
                        ;initialize input/output control words in the buffer
                        ;zero input/output data
      if EVM56K         ;for EVM56002 use MIC input
        ctrlcd  1,r2,BufLen,MIC,RxGain,RxGain,LINEO|HEADP,TxAttenuate,TxAttenuate
      else              ;for DSPCARD4 use LINE input
        ctrlcd  1,r2,BufLen,LINEI,RxGain,RxGain,LINEO|HEADP,TxAttenuate,TxAttenuate
      endif
        opencd SampleFreq/1000.0,HPF     ;start taking samples at given rate

        jmp <ProcessLoop

        if SPY

SpySync move a10,L:<SpySave     ;output: carry=1 => no spy request
        move a2,X:<SpySave+1    ;carry=0 => spy request !
        move x0,Y:<SpySave+1    ;512 words (jsr <SpyA) should follow
        move x1,Y:<SpyCount
        move X:<SpyCount,a
        tst a
        jne <SpyCont
        lookc 0
        jcs <Spy_end
        move #>'S',a
        cmp x0,a
        ori #$01,ccr
        jne <Spy_end
        move #>'P',x0
        putc
        move #>0,x0      ; send a 0 meaning non-complex data
        putc
        move #>512,a
        move a,X:<SpyCount
SpyCont andi #$FE,ccr
        jmp <Spy_end

SpyA    move a10,L:<SpySave
        move a2,X:<SpySave+1
        move x0,Y:<SpySave+1
        move x1,Y:<SpyCount
        move X:<SpyCount,a
        tst a
        jne <Spy_copy

Spy_check
        lookc 0
        jcs <Spy_end
        move #>'S',a
        cmp x0,a
        jne <Spy_end
        move #>'P',x0
        putc
        move #>0,x0      ; send a 0 meaning non-complex data
        putc
        move #>512,a
Spy_copy
        move #>1,x0
        sub x0,a
        move a,X:<SpyCount

        move X:<SpySave,a
        rep #8
          lsr a
        move a1,x0
	putc
        move X:<SpySave,a
        rep #16
          lsr a
        move a1,x0
        putc

Spy_end move L:<SpySave,a10
        move X:<SpySave+1,a2
        move Y:<SpySave+1,x0
        move Y:<SpyCount,x1
        rts

        endif

;*************************************************************************
;Internal data RAM

        LOMEM X:$0000,Y:$0000,L:$0000
        HIMEM X:$00FF,Y:$00FF,L:$00FF

        org L:user_data

        if SPY
SpySave dc 0,0
SpyCount dc 0
SpyTmp   ds 1
        endif

Rand    dc $954820584783        ;seed and increment for the random
RandInc dc $894659236475        ;number generator

RxTmp      ds 1

LastL = *
        org X:LastL
        org Y:LastL

        org X:
CodecPtr    dc Buffer

RxCarPhase  dc 0
RxCarFreq   dc 2.0*HilbertDecimate*RxCarrier/SampleFreq
RxScanStepFreq dc 2.0*HilbertDecimate*RxScanStep/SampleFreq

TxCarPhase  dc 0
TxCarFreq   dc 2.0*HilbertDecimate*TxCarrier/SampleFreq

RxSymbolFilterPtr dc RxSymbolFilterTap
TxSymbolFilterPtr dc TxSymbolFilterTap

	if UseNewTransmitter
;the 1.50 fudge factor gives us the same p-p deviation as
;the original transmit code.
TxDigLevel dc -TxSymbAmpl*1.50
	else
TxDigLevel dc TxSymbAmpl
	endif

RxState dc 0

RxDCDcounter dc 0

RxSamplePtr dc RxCorrel
RxCorrelPtr dc RxCorrel
RxProcPipePtr dc RxProcPipe

TxBitTime dc SymbolSepar

txdelay dc DefTxDelay	;runtime-patchable
txtail dc DefTxTail	;runtime-patchable
txduplex dc DefTxDuplex	;runtime-patchable

        if RxCtrlUpDown
RxUpDownPulse dc 0
RxUpDownHold  dc 0
        endif

        if RxAGC
RxAudioMS   dc 0
RxAGCcount  dc RxAGCfall
        endif

        if SPY_RxPhaseDiff
RxPrevPhase dc 0
        endif

        if SPY&&TxDrift
TxDriftStepFreq dc 2.0*HilbertDecimate*TxDriftStep/SampleFreq
        endif

        org X:
LastX = *
        org Y:
LastY = *
        if @cvi(LastX)>=@cvi(LastY)
          org L:LastX
        else
          org L:LastY
        endif

;*************************************************************************
;External L/X/Y data RAM

        if EVM56K
          LOMEM X:$2000,Y:$2000,L:$2000
          HIMEM X:$3FFF,Y:$3FFF,L:$3FFF
        else
          LOMEM X:$0200,Y:$0200,L:$0200
          HIMEM X:$1FFF,Y:$1FFF,L:$1FFF
        endif

      if EVM56K
        org L:$2000
      else
        org L:$200
      endif

RxSymbolFilterTap dsm SymbolShapeLen
TxSymbolFilterTap dsm SymbolShapeLen

RxCorrel  dsm SymbolSepar*4
RxCorrel0 dsm SymbolSepar*4

RxProcPipe dsm RxProcPipeLen*SymbolSepar

LastL = *
        org X:LastL
        org Y:LastL

        org X:
LastX = *
        org Y:
LastY = *
        if @cvi(LastX)>=@cvi(LastY)
          org L:LastX
        else
          org L:LastY
        endif

Buffer  dsm BufLen*4    ;CODEC's input/output buffer

;*************************************************************************
;External Y-only data RAM

        if EVM56K
          LOMEM Y:$0200
          HIMEM Y:$1FFF
        else
          LOMEM Y:$2000
          HIMEM Y:$3FFF
        endif

      if EVM56K
        org Y:$200
      else
        org Y:$2000
      endif

        org Y:
AliasFilterI dsm AliasFilterLen
AliasFilterQ dsm AliasFilterLen

        org Y:
RxSymbolShape  dsm SymbolShapeLen
TxSymbolShape  dsm SymbolShapeLen

;*************************************************************************
;constant tables: FIR and window shapes

LowFreq equ AliasLowFreq/SampleFreq
UppFreq equ AliasUppFreq/SampleFreq

w0      equ     0.35875 ;coeff. for Blackman-Harris 4 term
w1      equ     0.48829 ;minimum sidelobe window (copied from Motorola BBS)
w2      equ     0.14128
w3	equ	0.01168

        org Y:AliasFilterI

time = -@cvf(AliasFilterLen/2)+0.5
count   set 0
        dup AliasFilterLen
angle = PI*time/@cvf(AliasFilterLen/2)
Window = w0+w1*@cos(angle)+w2*@cos(2.0*angle)+w3*@cos(3.0*angle)
Filter = (@sin(2.0*PI*time*UppFreq)-@sin(2.0*PI*time*LowFreq))/(PI*time)
time = time+1.0
        dc  Window*Filter
count   set count+1
        endm

        org Y:AliasFilterQ

time = -@cvf(AliasFilterLen/2)+0.5
count   set 0
        dup AliasFilterLen
angle = PI*time/@cvf(AliasFilterLen/2)
Window = w0+w1*@cos(angle)+w2*@cos(2.0*angle)+w3*@cos(3.0*angle)
Filter = (-@cos(2.0*PI*time*UppFreq)+@cos(2.0*PI*time*LowFreq))/(PI*time)
time = time+1.0
        dc  Window*Filter
count   set count+1
        endm

s0      equ     0.35005138      ;Like Blackman-Harris but for minimal
s1      equ     0.50799695      ;crosstalk between symbols.
s2      equ     0.14499812      ;The Blackman-Harris is not that bad and
s3      equ    -0.01294745      ;the transmitted spectrum has no sidelobes.

        org Y:RxSymbolShape

time = -@cvf(SymbolShapeLen/2)+0.5
count   set 0
        dup SymbolShapeLen
angle = PI*time/@cvf(SymbolShapeLen/2)
Filter = s0+s1*@cos(angle)+s2*@cos(2.0*angle)+s3*@cos(3.0*angle)
;Filter = 0.75*@cos(PI*time/@cvf(SymbolShapeLen))+0.25*@cos(3*PI*time/@cvf(SymbolShapeLen))
time = time+1.0
        dc  Filter/(SymbolShapeLen/2)
count   set count+1
        endm

        org Y:TxSymbolShape

time = -@cvf(SymbolShapeLen/2)+1.0
count   set 0
        dup SymbolShapeLen
angle = PI*time/@cvf(SymbolShapeLen/2)
;Filter = 0.5+0.5*@cos(angle)
Filter = s0+s1*@cos(angle)+s2*@cos(2.0*angle)+s3*@cos(3.0*angle)
;Filter = 0.75*@cos(PI*time/@cvf(SymbolShapeLen))+0.25*@cos(3*PI*time/@cvf(SymbolShapeLen))
time = time+1.0
        dc  Filter
count   set count+1
        endm

;*************************************************************************

        end

