;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;
;   COPYRIGHT (C) 1994 KEN STATON    ;
;         ALL RIGHTS RESERVED        ;
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;

;
;*************************************************************************
;
; INPUT ACC = relative index number of digitized speech block
;
; MODIFIES REGISTERS: R3, R5, R6, ACC, PSW:C
; MODIFIES Variables: SP_INDX, SPSTRT_H, SPSTRT_L, SPSTOP_H, SPSTOP_L,
;			BLK_IDX, PREV, TMP1
;
;	2Feb94	Fixed time variation between output values
;

;MAKESOUND
	JB	SPEAK,DO_SAYIT	;Check if talking turned on/off
	RET			;Return from MAKESOUND if OFF

DO_SAYIT   
	CLR	INT_EN		;DISABLE INTS!
	PUSH	DPL		; SAVE DPTR
	PUSH	DPH

; MULT BY 4 TO INDEX INTO ARRAY WITH 4 BYTE ENTRIES...
	CLR     C
	RLC     A               ; MULT BY 2
	CLR     C
	RLC     A               ; MULT BY 2
	MOV     SP_INDX,A       ; SAVE INDEX*4 (ea entry is 4 bytes)

; SET DPTR TO START OF ARRAY...
	MOV     DPTR,#SPCH_TBL

; BUILD START & STOP...
	MOVC    A,@A+DPTR
	MOV     SPSTRT_H,A      ; KEEP HIGH START

	MOV     A,SP_INDX
	INC     A               ; MOVE TO LOW BYTE OF START
	MOVC    A,@A+DPTR
	MOV     SPSTRT_L,A      ; KEEP LOW START

	MOV     A,SP_INDX
	INC     A               ; MOVE TO HIGH BYTE OF STOP
	INC     A
	MOVC    A,@A+DPTR
	MOV     SPSTOP_H,A      ; KEEP HIGH STOP

	MOV     A,SP_INDX
	INC     A               ; MOVE TO LOW BYTE OF STOP
	INC     A
	INC     A
	MOVC    A,@A+DPTR
	MOV     SPSTOP_L,A      ; KEEP LOW STOP

; NOW SET DPTR TO START...
	MOV     DPL,SPSTRT_L    ; SET DPTR LOW
	MOV     DPH,SPSTRT_H    ; SET DPTR HIGH
	
	MOV     BLK_IDX,#0      ; RESET BLOCK COUNTER
	MOV     PREV,#REL_DC    ; START AT RELATIVE! DC LEVEL

;
; SPEAK LOOP
;
; START WITH TEST FOR END OF SPEECH BLOCK
;

SAYLOOP 
	MOV     A,SPSTOP_L      ; TEST IF DONE
	CJNE    A,DPL,SAYHIGHL
	MOV     A,SPSTOP_H
	CJNE    A,DPH,SAYHIGHH

;DONE!
	MOV     P1,#ABS_DC	; SET OUTPUT TO DC LEVEL WHEN DONE

	POP	DPH		; CLEANUP - RESTORE DPTR
	POP	DPL
	SETB	INT_EN		; ENABLE INTS
	RET			; DONE SPEAKING

;
; Speak HIGH NYBBLE
;

SAYHIGHL
	NOP			;delay for high mov equiv
	NOP			;delay for high cjne equiv
	NOP
SAYHIGHH
	MOV     A,BLK_IDX       ; TEST FOR END OF BLOCK
	CJNE    A,#HALFBLK,NOBLKRST
	MOV     BLK_IDX,#0      ; RESET BLOCK COUNTER FOR NEXT BLOCK
NOBLKRST
	CLR     A
	MOVC    A,@A+DPTR       ; GET BYTE

	SWAP	A		; SHIFT UPPER NYBBLE TO LOW 4 BITS
	ANL     A,#0FH          ; MASK OUT UPPER 4 BITS
	MOV     TMP1,A		; TEMP SAVE HIGH DATA NYBBLE

	MOV     A,BLK_IDX       ; TEST IF Qfn
	CJNE    A,#0,NOTQFNH
	MOV     R3,TMP1		;SET QFN FROM SAVED HIGH NYBBLE
	SJMP    SAYLOW

NOTQFNH
	MOV     A,TMP1		; RESTORE HIGH DATA NYBBLE
	LCALL   INVQFN
	CLR     C
	ADDC    A,PREV          ; A=A+C+PREV
	MOV     PREV,A          ; SO, PREV=PREV+A

	LCALL   REL2ABS         ; CONVERT TO UNSIGNED ABS
	MOV     P1,A            ; OUTPUT BYTE

				;
	MOV     R6,#SP_DLY      ; SP_WAIT
SPLOOPH DJNZ    R6,SPLOOPH      ;
				;

; NOW Speak LOW NYBBLE...

	NOP
	NOP
	NOP
	NOP
	NOP			; delay for next byte equiv
SAYLOW
	CLR     A
	MOVC    A,@A+DPTR       ; GET BYTE
	NOP			; delay for high swap equiv

	ANL     A,#0FH          ; MASK OUT UPPER 4 BITS
	NOP
	NOP
	NOP
	NOP
	NOP			; delay for high qfn test equiv
	LCALL   INVQFN
	CLR     C
	ADDC    A,PREV          ; A=A+C+PREV
	MOV     PREV,A          ; SO, PREV=PREV+A

	LCALL   REL2ABS         ; CONVERT TO UNSIGNED ABS
	MOV     P1,A            ; OUTPUT BYTE

				;
	MOV     R6,#SP_DLY      ; SP_WAIT
SPLOOPL DJNZ    R6,SPLOOPL      ;
				;

; GET NEXT TWO NYBBLES (BYTE)

	INC     BLK_IDX
	INC     DPTR            ; MUST INC PTR, > 256 BYTES

	LJMP	SAYLOOP		; REPEAT

;****************************************************************
;
; SPEAK SUPPORT SUBROUTINES....
;

;****************************************************************
;
; REL2ABS
;
; CONVERTS RELATIVE (SIGNED) NUMBERS TO ABSOLUTE (UNSIGNED)
;       DPCM IS DONE USING SIGNED NUMBERS (2's complement)
;       SPEECH DIGITIZATION IS DONE USING UNSIGNED NUMBERS
; INPUT IS IN ACC
; OUTPUT IS IN ACC
;
;	Signed (DEC)	Signed (HEX)	UNSigned
;	-1		$FF		$7F
;	-2		$FE		$7E
;	 .				 .
;	 .				 .
;	-127		$81		$01
;	-128		$80		$00
;	+127		$7F		$FF
;	+126		$7E		$FE
;	 .				 .
;	 .				 .
;	+1		$01		$81
;	 0		$00		$80
;

REL2ABS
	CLR     C
	ADDC    A,#128
	RET


;
; INVERSE QUANTIZATION FUNCTION
; INPUT IN ACC
; OUTPUT IN ACC
;

INVQFN
	INC	A		; TABLE INDEX 0 = RET, SO OFFSET BY 1
	CJNE	R3,#7,TRY6
	NOP	;1
	NOP	;2
	NOP	;3
	NOP	;4
	NOP	;5
	NOP	;6
	NOP	;7
	NOP	;8
	NOP	;9
	NOP	;10
	LCALL	IQBASE7
	RET
TRY6	CJNE	R3,#6,TRY5
	NOP	;1
	NOP	;2
	NOP	;3
	NOP	;4
	NOP	;5
	NOP	;6
	NOP	;7
	NOP	;8
	LCALL	IQBASE6
	RET
TRY5	CJNE	R3,#5,TRY4
	NOP	;1
	NOP	;2
	NOP	;3
	NOP	;4
	NOP	;5
	NOP	;6
	LCALL	IQBASE5
	RET
TRY4	CJNE    R3,#4,TRY3
	NOP	;1
	NOP	;2
	NOP	;3
	NOP	;4
	LCALL	IQBASE4
	RET
TRY3    CJNE    R3,#3,TRY2
	NOP	;1
	NOP	;2
	LCALL	IQBASE3
	RET
TRY2    CJNE    R3,#2,TRY1
	LCALL	IQBASE2
	RET
TRY1
	LCALL	IQBASE1
	RET

IQBASE1	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     2
	.DB     3
	.DB     4
	.DB     5
	.DB     6
	.DB     7
	.DB     -8              ; 1111 1000
	.DB     -7              ; 1111 1001
	.DB     -6              ; 1111 1010
	.DB     -5              ; 1111 1011
	.DB     -4              ; 1111 1100
	.DB     -3              ; 1111 1101
	.DB     -2              ; 1111 1110
	.DB     -1              ; 1111 1111

IQBASE2	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     2
	.DB     3
	.DB     4
	.DB     6
	.DB     8
	.DB     12
	.DB     -13
	.DB     -10
	.DB     -7
	.DB     -5
	.DB     -4
	.DB     -3
	.DB     -2
	.DB     -1

IQBASE3	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     2
	.DB     3
	.DB     6
	.DB     9
	.DB     15
	.DB     24
	.DB     -26
	.DB     -17
	.DB     -11
	.DB     -7
	.DB     -5
	.DB     -3
	.DB     -2
	.DB     -1

IQBASE4	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     2
	.DB     4
	.DB     7
	.DB     12
	.DB     21
	.DB     36
	.DB     -38
	.DB     -23
	.DB     -14
	.DB     -9
	.DB     -5
	.DB     -3
	.DB     -2
	.DB     -1

IQBASE5	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     2
	.DB     4
	.DB     8
	.DB     14
	.DB     26
	.DB     47
	.DB     -49
	.DB     -29
	.DB     -17
	.DB     -10
	.DB     -6
	.DB     -4
	.DB     -2
	.DB     -1

IQBASE6	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     3
	.DB     5
	.DB     10
	.DB     19
	.DB     36
	.DB     69
	.DB     -72
	.DB     -41
	.DB     -23
	.DB     -13
	.DB     -7
	.DB     -4
	.DB     -2
	.DB     -1

IQBASE7	MOVC	A,@A+PC
	RET
	.DB     0
	.DB     1
	.DB     3
	.DB     6
	.DB     11
	.DB     23
	.DB     45
	.DB     90
	.DB     -95
	.DB     -52
	.DB     -28
	.DB     -15
	.DB     -8
	.DB     -5
	.DB     -2
	.DB     -1

;
; SPEECH TABLE.  *** MUST *** have the same order as CODE_TBL.
; 26+10+3+5=44 ENTRIES @ 4 BYTES EACH = 176 BYTES TOTAL.
; SPEECH.TXT is compiled from a text source file into a text
; assembler source file and a binary data file using SPCH_LIB.C
;

SPCH_TBL
#INCLUDE "SPEECH.TXT"


