;
;       PACKET VOICE MODULE SOFTWARE
;
;       Written by Harvind Samra
;       School of Electrical Engineering
;       University of Kansas
;       May 31, 1996
;
;       Used to operate packet voice module in the
;       "Packet Voice Communication System"
;
;       Contains code for CVSD compression and 
;       decompression and code for RS-232
;       encoding and decoding
;

	.mmregs
	.ds 0d00h

rsmask  .word   0       ; bitmask/state variable used for RS-232 comm.
mode    .word   0       ; 0=recv. mode, 1=trans. mode
cmpmask .word   1       ; bitmask/state variable used in CVSD comp. and decomp.
bdstate .word   0       ; state variable used to maintain baud rate
temp1   .word   0       ; temporary variables
temp2   .word   0
x       .word   0       ; contains current voice(A/D) sample

; Following variables used to set baud rate
; with Timer Interrupt.  Uses rate2 2/3 of
; the time and rate1 1/3 of the time to create
; baud rate by dividing down a 20Mhz clock.

rate2   .word   1041    ; for 19,200 baud
rate1   .word   1040    ; for 19,200 baud

;rate2  .word   2081    ; for 9,600 baud
;rate1  .word   2082    ; for 9,600 baud

;rate2  .word   4163    ; for 4,800 baud
;rate1  .word   4162    ; for 4,800 baud

;rate2  .word   8325    ; for 2,400 baud
;rate1  .word   8326    ; for 2,400 baud

;rate2  .word   16651   ; for 1,200 baud
;rate1  .word   16650   ; for 1,200 baud

currval .word   0       ; used to store curr. byte being trans. or recv.
byt     .word   0       ; used to store curr. byte being compressed

; CVSD algorithm variables
p       .word   0       ; 
H1      .word   29491   ; 0.9*32767/128
step    .word   0       ; current quantization level
stepsz  .word   20      ; increment for quantization level, 0.08*32767/128
stepmax .word   2560    ; 32767*10/128
stepmin .word   0
alpha   .word   0
beta    .word   32604   ; 0.995*32768
b0      .word   32767
b1      .word   32767
b2      .word   32767


cr      .word   13
passc   .word   22      ; PASS character for TNC
stop    .word   1       ; STOP character
flag    .word   58      ; ASCII value for ':'
valid   .word   0       ; Is data being received from TNC valid voice data?
strtrcv .word   0       ; state variable, indicates that START char. recv'd.

; Message buffer
begin   .word   107     ; ASCII value for 'k'
	.word   13      ; ASCII value for CR
	.word   254     ; START character
	.word   254     ; START character
start   .word   254     ; START character
dstart  .word   0       ; Data begins here

	.ds     2a00h
; these are filled with STOP characters
last3   .word   0
last2   .word   0
last1   .word   0
last    .word   13

	.ps     0808h
	RETE
timint  RETE            ; needed to reference timint to addr. 0809h

	.ps     0808h
	B       RECV_TIM_HDLR   ; timer interrupt vector
	.ps     0806h
	B       EXT_INT_HDLR    ; external interrupt(PTT switch) vector
	.ps     080ah
	B       AD_COMP_HDLR    ; A/D interrupt vector, recv. for DSK serial port
	.ps     080ch
DAINT   RETE            ; D/A interrupt vector, trans. for DSK serial port
daint1  RETE

;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAIN PROGRAM;;;;;;;;;;;;;;;;;;;;;;;;;        
	.ps     0a00h
	.entry

; Initialize DSK board 
DSK_INIT        
	SETC    INTM
	LDP     #0
	LACC    #20h    ; Enable serial port transmit(D/A) interrupt
	SAMM    IMR     ;
	CLRC    INTM
	SPLK    #20h,TCR; Set Clock frequency to 20Mhz
	LACC    #1      ;
	LDP     #0      ;
	SAMM    PRD     ;
	OPL     #0834h,PMST
	LACC    #0
	SAMM    CWSR    ; Set software wait state to 0
	SAMM    PDWSR   ;
	LACC    #8008h  ; Initialize DSK serial port
	SAMM    SPC     ;
	ADD     #00c0h  ;
	SAMM    SPC     ;
	 
; Initialize AIC(analog interface circuit) module
; Set A/D and D/A sampling rate to 16kHz
AIC_INIT        
	LACC    #0003h  ; Set AIC's TB and RB register
	SAMM    DXR     ;
	IDLE            ;
	LACC    #2852h  ; 
	SAMM    DXR     ;
	IDLE            ;          
	LACC    #0003h  ; Set AIC's TA and RA register
	SAMM    DXR     ;
	IDLE            ;
	LACC    #2040h  ;
	SAMM    DXR     ;
	IDLE            ;
	LACC    #0003h  ; Set AIC's control register
	SAMM    DXR     ;
	IDLE            ;
	LACC    #0023h  ;
	SAMM    DXR     ;
	IDLE            ;

	CLRC    OVM              ; OVM = 0
	SPM     0                ; PM = 0
	SETC    XF
	ZAP

	LDP     #DAINT          ; Set D/A intr. vec. to "B DA_DECOMP_HDLR"
	SPLK    #7980h,DAINT    
	SPLK    DA_DECOMP_HDLR,daint1   

	LDP     #rate2                  
	SPLK    #0,valid        ; Initialize program variables
	SPLK    #0,rsmask
	SPLK    #0,strtrcv
	SPLK    #0,mode

	LAR     AR0,#dstart     ; Set Aux. Reg. to beginning of message buffer
				; AR0 used as address counter for buffer
	SPLK    #0,bdstate
	LACC    rate2           ; Set baud rate, set timer intr. to 20Mhz/rate2
	SAMM    PRD
	LACC    #0ch            ; Enable exterinal and timer interrupts
	SAMM    IMR
	SAMM    IFR
	CLRC    INTM
	ZAP

LOOP    LACC    mode            ; Receive=0, Transmit=1
	BCND    TRNS,NEQ                
	LACC    p
	AND     #0fffch
	SAMM    DXR
TRNS    IDLE                    ; If transmit, go here
	B       LOOP

; Ext. interrupt routine to beginning compression 
; and transmission of voice message
EXT_INT_HDLR
	LDP     #cmpmask
	LACC    #1              ; Reset timer intr. to 20Mhz so A/D works
	SAMM    PRD
	SPLK    #1,cmpmask      ; Reload compressor
	SPLK    #0,byt
	SPLK    #0,p
	SPLK    #0,step
	SPLK    #0,alpha
	SPLK    #1,mode
	SPLK    #7fffh,b0       ; Set b0=b1=b2=1
	SPLK    #7fffh,b1
	SPLK    #7fffh,b2
	MAR     *,AR0           ; Set Aux. Reg. 0 to just after start char. 
	LAR     AR0,#start      ;  in message buffer
	MAR     *+,AR0
	LACC    #014h           ; Enable A/D and Timer Intr.,
	SAMM    IMR             ;  disable external intr.
	SAMM    IFR
	SETC    XF
	LACC    #ISR_RE_ENTER_E ; Set ARCR to point to variable last
	PUSH                    ; Done using stack since ARCR value
	RETI                    ; is popped after intr. is left
ISR_RE_ENTER_E
	LACC    #last   
	SAMM    ARCR
	CLRC    INTM
	RET

; Timer interrupt routine to send compressed samples
;  to TNC with RS-232 encoding
;  XF pin is connected to TNC's RS232 input
TRANS_TIM_HDLR
	LDP     #rsmask
	LACC    rsmask
	BCND    NOSTART,NEQ     ; Send start bit?
	CLRC    XF              ; Yes
	LACC    *+,0,AR0        ; Store next comp. byte in currval
	SACL    currval
	SPLK    #1,rsmask       
	B       RESET           
NOSTART LACC    rsmask          ; Already sent start bit
	XOR     #100h   
	BCND    STOPBIT,EQ      ; Send stop bit?
	LACC    currval         ; No
	AND     rsmask          ; Apply rsmask bitmask to get next bit to send
	BCND    HIGH,NEQ
LOW     CLRC    XF              ; Send a zero
	LACC    rsmask
	SFL
	SACL    rsmask          
	B       RESET
HIGH    SETC    XF              ; Send a one
	LACC    rsmask
	SFL
	SACL    rsmask
	B       RESET
STOPBIT SPLK    #0,rsmask       ; Send stop bit
	SETC    XF      
	CMPR    2               ; End of message buffer?
	NOP
	BCND    DONE,TC                 
	B       RESET           ; No
DONE    LACC    #1              ; Yes, Turn off timer intr. so A/D works
	SAMM    PRD
	LDP     #DAINT          
	SPLK    RECV_TIM_HDLR,timint    ; Set timer intr. vector to "B RECV_TIM_HDLR"
	LDP     #rate2  
	SPLK    #0,valid        ; Reset variables
	SPLK    #0,rsmask
	SPLK    #0,strtrcv
	LAR     AR0,#dstart     ; Reset Aux. Reg. 0 for next message
	SPLK    #0,bdstate
	LACC    #0ch            ; Enable Ext. intr. and Timer intr.
	SAMM    IMR
	SAMM    IFR
	RETE                    ; Return from interrupt
RESET   LACC    bdstate         ; Resets timer interrupt counter
	ADD     #1              ; 2 interrupts for rate2 for every
	SACL    bdstate         ;  one interrupt at rate1
	XOR     #3
	BCND    RATE_2,NEQ
	LACC    rate1
	SAMM    PRD
	SPLK    #0,bdstate
	RETE
RATE_2  LACC    rate2
	SAMM    PRD
FINISH  RETE

; A/D interrupt routine to take voice samples, 
;  compress them, and store them in message buffer
AD_COMP_HDLR  
	LDP     #rsmask
	ZAP                     ; start of CVSD compression algorithm
	MAC     p,H1            ; Calculate next value of p
	MAC     step,b0
	APAC
	RPT     #14
	SFR
	SACL    p
	SPLK    #8000h,b0       ; Set b0 = -1
	LAMM    DRR
	SACL    x
	LACC    x,16            ; Find x-p
	SUB     p,16
	SUB     step,16         ; One-bit quantizer
	NOP                     
	XC      2,GT            
	SPLK    #7fffh,b0       
	SPLK    #0,alpha        
	SPLK    #0,temp1
	SPLK    #0,temp2
	LACC    b0              ; Calculate alpha
	XOR     b1
	NOP
	XC      2,EQ
	SPLK    #1,temp1
	LACC    b1
	XOR     b2
	NOP
	XC      2,EQ
	SPLK    #1,temp2
	LACC    temp1
	AND     temp2
	NOP
	XC      2,NEQ
	SPLK    #7fffh,alpha
	ZAP
	MAC     beta,step       ; Calculate step=next quant. level
	MAC     alpha,stepsz
	APAC
	RPT     #14
	SFR
	SACL    step
	LACC    step,16         ; Check step is within max. and min. values
	SUB     stepmax,16
	NOP
	XC      2,GT
	BLDD    #stepmax,step
	LACC    step,16
	SUB     stepmin,16
	NOP
	XC      2,LT
	BLDD    #stepmin,step
	BLDD    #b1,b2          ; Delay blocks for b(n)
	BLDD    #b0,b1
	LACC    b0              
	AND     cmpmask         ; Apply bitmask to store b0 into byt,
	OR      byt             ;  which is byte stored into message buffer
	SACL    byt
	LACC    cmpmask
	SFL
	SACL    cmpmask
	XOR     #100h           ; Filled a byte?
	BCND    RETURN,NEQ      ; If no, goto RETURN
	LACC    byt             ; Yes
	BCND    CRCHK,NEQ       ; Is byt=0?
	ADD     #2              ; then set byt=2 since 0 is NULL char,
	SACL    byt             ;  and TNC won't transmit it
CRCHK   LACC    byt             ; If byt=13 (ASCII for carriage return)
	SUB     cr              ;  toggle LSB of byt
	BCND    NOTCR,NEQ
	LACC    byt
	XOR     #1
	SACL    byt
NOTCR   LACC    byt             ; If byt=STOP char, then toggle LSB of byt
	SUB     stop
	BCND    NOTSTOP,NEQ
	LACC    byt
	XOR     #1
	SACL    byt
NOTSTOP LACC    byt             ; If byt=PASS char, then toggle LSB of byt
	SUB     passc
	BCND    NOTPASS,NEQ
	LACC    byt
	XOR     #1
	SACL    byt
NOTPASS LACC    byt             ; If byt=18h,19h,03h,08h,12h,11h,7ch,7eh,ffh
	XOR     #18h            ;  then put PASS char. ahead of byt.
	BCND    PASS,EQ         ;  TNC will interpret these six char.
	LACC    byt             ;  as control char. and will not
	XOR     #19h            ;  transmit them w/o a PASS char. in front
	BCND    PASS,EQ         ;  of any instance of these characters
	LACC    byt
	XOR     #03h
	BCND    PASS,EQ
	LACC    byt
	XOR     #08h
	BCND    PASS,EQ
	LACC    byt
	XOR     #12h
	BCND    PASS,EQ
	LACC    byt
	XOR     #11h
	BCND    PASS,EQ
	LACC    byt
	XOR     #13h
	BCND    PASS,EQ
	LACC    byt
	XOR     #7ch
	BCND    PASS,EQ
	LACC    byt
	XOR     #7eh
	BCND    PASS,EQ
	LACC    byt
	XOR     #0ffh
	BCND    STORE,NEQ
	LACC    byt
	SUB     #2
	SACL    byt
STORE   LACC    byt             ; Store byt into message buffer
	SACL    *+,0,AR0        ; and increment Aux. Reg 0
	SPLK    #1,cmpmask
	SPLK    #0,byt
RETURN  CMPR    0               ; Reached end of message buffer?
	NOP
	BCND    END_MB,TC
	CMPR    2
	NOP
	BCND    END_MB,TC
	RETE                    ; Return from interrupt, wait for next sample
END_MB  LACC    #04h            ; Yes
	SAMM    IMR             ; Kill A/D interrupt
	LAR     AR0,#last3      ; Place stop characters at end of buffer
	MAR     *,AR0 
	LACC    stop
	SACL    *+,0,AR0
	SACL    *+,0,AR0
	SACL    *+,0,AR0
	LACC    cr              ; Place carriage return at end of buffer to 
	SACL    *,0,AR0         ;  tell TNC to transmit remaining data
	LAR     AR0,#begin      ; Set Aux. Reg. 0 to beginning of messg. buffer
	MAR     *,AR0
	LACC    rate2           ; Setup timer interrupt to transmit message
	SAMM    PRD             ; setup baud rate
	SPLK    #0,rsmask
	SPLK    #0,mode
	LDP     #DAINT          ; Set tim. intr. vector to "B TRANS_TIM_HDLR"
	SPLK    TRANS_TIM_HDLR,timint
	LDP     #rsmask
	LACC    #08h            ; Enable timer interrupt
	SAMM    IMR
	SAMM    IFR
	RETE
PASS    LACC    passc           ; Store PASS character
	SACL    *+,0,AR0
	B       STORE
	
; Timer Interrupt Routine to received data from TNC
;  remove RS-232 coding, and store data into message bufffer 
;  BIO pin is connected to TNC's RS232 output
RECV_TIM_HDLR
	LDP     #rsmask
	LACC    rsmask
	BCND    STRT,EQ         ; Waiting for start bit?
	XOR     #100h
	BCND    STP,EQ          ; Waiting for stop bit?
HIGHBIT BCND    LOWBIT,BIO      ; Bit=1 
	LACC    rsmask          ; Used bitmask rsmask to place bit into currval
	AND     #0fffh          ;  to be later stored into message buffer
	ADD     currval
	SACL    currval
	LACC    rsmask          ; Shift bitmask
	SFL
	SACL    rsmask
	B       RESET_R
LOWBIT  LACC    rsmask          ; Shift bitmask
	SFL
	SACL    rsmask
	B       RESET_R
STP     XC      2,BIO           ; Waiting for stop bit(stop bit=0)
	B       RESET_R
	LACC    start           ; Check if currval=START char.
	XOR     currval
	BCND    GO,NEQ          
	LACC    strtrcv         ; Yes.  Check if 1st START char. in message
	BCND    GO,NEQ
	SPLK    #1,strtrcv              ; Yes.  Set valid=1 to begin storing recv'd data
	SPLK    #1,valid
	B       PORTRST
GO      LACC    strtrcv         ; Has START char been recv'd?
	BCND    PORTRST,EQ
	LACC    stop            ; Yes.  Is current byte a STOP char.
	XOR     currval
	BCND    KILLTIM,EQ      
	LACC    valid           ; No. Should current byte be saved?
	BCND    SAVING,NEQ      
FILTER  LACC    currval         ; No.  Wait until flag char. is recv'd.
	SUB     flag
	NOP
	XC      2,EQ            ; Has flag char. been recv'd?
	SPLK    #1,valid        ; Yes.
PORTRST SPLK    #0,currval      ; Reset currval and rsmask
	SPLK    #0,rsmask
	B       RESET_R
SAVING  LACC    currval         ; Is current byte a carriage return?
	SUB     cr
	NOP
	XC      2,EQ            
	SPLK    #0,valid        ; Yes. Set valid=0 to wait until next flag char.
	XC      2,EQ
	B       PORTRST         
	LACC    currval         ; Store current byte into message buffer
	SACL    *+,0,AR0        ; Increment Aux. Reg. 0 
	B       PORTRST
STRT    XC      2,BIO           ; Does BIO=start bit=1
	SPLK    #1,rsmask       ; Set bitmask rsmask to begin saving bits
	B       RESET_R
KILLTIM 
	SPLK    #1,cmpmask        ; Have recv'd entire message
	SPLK    #0,byt          ; Initialize CVSD decompression algorithm
	SPLK    #0,p
	SPLK    #0,step
	SPLK    #0,alpha
	SPLK    #7fffh,b0
	SPLK    #7fffh,b1
	SPLK    #7fffh,b2
	LACC    #24h            ; Enable D/A and External Interrupts
	SAMM    IMR
	ADD     #8h
	SAMM    IFR
	LACC    #1              ; Set timer int. to 20Mhz for D/A converter
	SAMM    PRD
	LACC    #ISR_RE_ENTER   
	PUSH
	RETI
ISR_RE_ENTER
	MAR     *,AR0
	SAR     AR0,rsmask      
	LACC    rsmask
	SAMM    ARCR            ; Set ARCR to point to end of message buffer
	LAR     AR0,#dstart     ; Set Aux. Reg. 0 to beginning of message buffer
	MAR     *,AR0
	LACC    #0
	SAMM    DXR
	CLRC    INTM
	RET
RESET_R LACC    bdstate         ; Resets timer interrupt counter
	ADD     #1              ; 2 interrupts at rate2 for every
	SACL    bdstate         ;  1 interrupt at rate1
	XOR     #3
	BCND    RATE_R,NEQ
	LACC    rate1
	SAMM    PRD
	SPLK    #0,bdstate
	RETE
RATE_R  LACC    rate2
	SAMM    PRD
	RETE

; D/A Interrupt routine to decompress data
;  in message buffer and play through D/A converter.
DA_DECOMP_HDLR
	LDP     #alpha
	CMPR    2               ; At end of message buffer?
	NOP
	BCND    KILLDA,TC       ; Yes. End D/A section.
	LACC    *,0,AR0         ; No.
	AND     cmpmask         ; Apply bitmask to extract next b(n) or b0
	NOP
	XC      2,NEQ           
	SPLK    #7fffh,b0       ; Set b0 = 1
	XC      2,EQ
	SPLK    #8000h,b0       ; Set b0 = -1
	SPLK    #0,alpha        ; Calculate alpha
	SPLK    #0,temp1
	SPLK    #0,temp2
	LACC    b0
	XOR     b1
	NOP
	XC      2,EQ
	SPLK    #1,temp1
	LACC    b1
	XOR     b2
	NOP
	XC      2,EQ
	SPLK    #1,temp2
	LACC    temp1
	AND     temp2
	NOP
	XC      2,NEQ
	SPLK    #7fffh,alpha
	ZAP
	MAC     beta,step       ; Calculate step, next quantization level
	MAC     alpha,stepsz
	APAC
	RPT     #14
	SFR
	SACL    step
	LACC    step,16         ; Keep step between min. and max.
	SUB     stepmax,16
	NOP
	XC      2,GT
	BLDD    #stepmax,step
	LACC    step,16
	SUB     stepmin,16
	NOP
	XC      2,LT
	BLDD    #stepmin,step
	BLDD    #b1,b2          ; Delay blocks for b(n)
	BLDD    #b0,b1
	ZAP
	MAC     p,H1            ; Calculate p(n)
	MAC     step,b0
	APAC
	RPT     #14
	SFR
	SACL    p
	LACC    cmpmask         ; End of byte from message buffer?
	SFL
	SACL    cmpmask
	XOR     #100h
	NOP
	XC      2,EQ            ; Yes. Reset the variable cmpmask
	SPLK    #1,cmpmask
	XC      1,EQ            ; Increment Aux. Reg. 0 to next byte
	MAR     *+,AR0
	RETE
KILLDA
	SPLK    #0,p            ; Have played back recv'd message
	LDP     #rate2          
	SPLK    #0,valid        ; Reset variables
	SPLK    #0,rsmask
	SPLK    #0,strtrcv
	LAR     AR0,#dstart     ; Set Aux. Reg.0 to start of data buffer
	SPLK    #0,bdstate
	LACC    rate2           ; Set timer interrupt counter to rate2
	SAMM    PRD
	LACC    #0ch            ; Enable external and timer interrupts
	SAMM    IMR
	SAMM    IFR
	RETE
	.END    
