
;
; $Header: E:/HAM/EVM/RCS/tone.asm 1.5 1997/02/22 15:11:40 dbraun Exp $
;
; $Log: tone.asm $
; Revision 1.5  1997/02/22 15:11:40  dbraun
; Added SPYing of L or R input channels, or output channel
; (spyleft, spyright, or spyout runtime-patchable parameters).
;
; Revision 1.4  1997/02/21 23:03:29  dbraun
; Made spy output runtime selectable (dl tone spy=1)
;
; Revision 1.3  1997/02/21 22:59:28  dbraun
; Added: PTT control and second tone, both controllable with
; run-time parameters, and also SPY option.
;
; Revision 1.2  1996/12/28 15:57:26  dbraun
; Some improvements
;
; Revision 1.1  1996/12/06 09:32:16  dbraun
; Initial revision
;
;

        opt  mu

        page 132

        nolist
	include 'leonid'
	list

        scsjmp short    ;force short jumps for .if/.endi, .while/.endw, etc.

        title 'Continous tone output'

;*************************************************************************
;compile-time constants

SampleFreq equ 48000.0

DefaultFreq equ 1000    ;(integer) default output frequency in Hz
DefaultFreq2 equ 1500    ;(integer) default output frequency in Hz
DefaultRMS equ 0.35355  ;(float) default output level in RMS volts
			;Note that the codec OLB parameter is set
			;to give a maximum P-P output of 2.8 volts,
			;or a RMS level of 1.0 volts.
			; (default of 0.35355 = 1.0V p-p)


TxAttenuate     equ  0.0 ;[dB] attenuate the output CODEC's signal.
BufLen   equ    32


PTTRight     macro mode      ;PTT line: clr/set/chg
        b\mode #4,X:$FFE4
        endm

PTTLeft     macro mode      ;PTT line: clr/set/chg
        b\mode #0,X:$FFE4
        endm


;*************************************************************************
;The actuall code (internal/external program RAM)

        LOMEM P:$0000
        HIMEM P:$1FFF

        org     p:user_code

;initialize registers, buffers, etc.

        ori #%00000100,omr      ;enable the sine ROM table
                                ;this is for the IQ routine

; 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.

	btst	#0,X:<dig
	.if <cs>
	  bset	#9,X:$ffe4 
  	.endi

	; Activate the transmitter(s) if desired 
	btst	#0,X:<lptt
	.if <cs>
	  PTTLeft set
  	.endi

	btst	#0,X:<rptt
	.if <cs>
	  PTTRight set
  	.endi


        ; Calculate CarFreq from f (see below)
        ; Some trickiness is required to avoid overflow
        ; or rounding errors.  Note that any change to sample
        ; frequency will require changing this a bit.

        move X:<f,x0   ; Interpreted as float, x0 = f * 2^^-22
        move #(2<<13)*2.0/SampleFreq,y0
        mpy x0,y0,a  ; a = 2 * f * 1/SampleFreq * 2^^13 * 2^^-22
        	     ; or: a = 2 * f * 1/SampleFreq * 2^^-9

        rep #9	     ; Multiply a by 2^^9
          asl a      ; This does not lose bits because a is 48 bits long
        rnd a	     ; round

  	move a1,X:<CarFreq


        move X:<f2,x0   ; Interpreted as float, x0 = f2 * 2^^-22
        move #(2<<13)*2.0/SampleFreq,y0
        mpy x0,y0,a  ; a = 2 * f2 * 1/SampleFreq * 2^^13 * 2^^-22
        	     ; or: a = 2 * f2 * 1/SampleFreq * 2^^-9

        rep #9	     ; Multiply a by 2^^9
          asl a      ; This does not lose bits because a is 48 bits long
        rnd a	     ; round

  	move a1,X:<CarFreq2



        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 #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,0.0,0.0,LINEO|HEADP,TxAttenuate,TxAttenuate
      else              ;for DSPCARD4 use LINE input
        ctrlcd  1,r2,BufLen,LINEI,0.0,0.0,LINEO|HEADP,TxAttenuate,TxAttenuate
      endif
        opencd SampleFreq/1000.0,HPF     ;start taking samples at given rate


ProcessLoop     ;main loop to process CODEC's samples
        waitblk r2,BufLen,1

; If the user specified, spy on the input

	btst	#0,X:<spyleft
	.if <cs>
	  bclr	#0,X:<spyout  ; spy on only one thing on a time
	  bclr	#0,X:<spyright
  	  move X:(r2),a
  	  jsr <SpyA
	.endi

	btst	#0,X:<spyright
	.if <cs>
	  bclr	#0,X:<spyout  ; spy on only one thing on a time
	  bclr	#0,X:<spyleft
	  move (r2)+
	  move X:(r2)-,a
	  jsr <SpyA
        .endi

; Generate the tone(s).

        move X:<CarPhase,x0  ;get the transmitter carrier phase
        move X:<CarFreq,a    ;advance the phase
        add x0,a 
        move a1,X:<CarPhase
        jsr <IQ               ;compute I and Q (modifies a,b,x,y,r0,m0,n0)

        move a1,x0	     ;scale the output by scale factor rms
        move X:<rms,y0
        mpyr x0,y0,a

	move (r2)+
	move a,Y:(r2)+	   ;output to both channels
	move a,Y:(r2)-	   ; leave r2 pointing to the first channel

        move X:<CarPhase2,x0  ;get the other transmitter carrier phase
        move X:<CarFreq2,a    ;advance the phase
        add x0,a 
        move a1,X:<CarPhase2
        jsr <IQ               ;compute I and Q (modifies a,b,x,y,r0,m0,n0)

        move a1,x0	     ;scale the output by scale factor rms2
        move X:<rms2,y0
        mpy x0,y0,a

  	move Y:(r2),x0	    ; mix the 2 tones.
  	add x0,a

	btst	#0,X:<spyout
	.if <cs>
	  bclr	#0,X:<spyleft  ; spy on only one thing on a time
	  bclr	#0,X:<spyright
  	  jsr <SpyA
        .endi

	move a,Y:(r2)+	   ;output to both channels
	move a,Y:(r2)+
        move (r2)+

        jmp <ProcessLoop


;-----------------------------------------------------------------------------

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"


; Official, certified correct SPY code!!!

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
        rti		;N1OWU: the RTI instruction restores the SR



;*************************************************************************
;Internal data RAM

        LOMEM X:$0000,Y:$0000,L:$0000
        HIMEM X:$00FF,Y:$00FF,L:$00FF

        org L:user_data

SpySave dc 0,0
SpyCount dc 0
SpyTmp   ds 1

LastL	= *
	org     X:LastL
	org	Y:LastL



        org X:

CarPhase  dc 0
CarPhase2  dc 0

; These are meant to be patched at run time: freq and RMS amplitude
f	  dc DefaultFreq  ; 1 to ~22000 Hz
f2	  dc DefaultFreq2  ; 1 to ~22000 Hz
rms	  dc DefaultRMS ; 0.0 to 0.99999 RMS volts
rms2	  dc 0.0        ; 0.0 to 0.99999 RMS volts
dig	  dc 1           ; 0 for mic-level output, 1 for digital (line) level

lptt	dc 0		; set one or both to 1 to activate transmitter
rptt	dc 0

spyout	dc 0		; set to 1 to look at output with SPY.EXE
spyleft	dc 0		; set to 1 to look at left channel input with SPY.EXE
spyright dc 0		; set to 1 to look at right channel input with SPY.EXE

; This (cycles/sample) floating-point value is calculated from f, above.
; CarFreq = 2 * f / SampleFreq
CarFreq   dc 0
CarFreq2   dc 0


;*************************************************************************
;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


Buffer  dsm BufLen*4    ;CODEC's input/output buffer


        end

