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

#INCLUDE "GLOBALS.TXT"
	.MSFIRST                ; MOST SIGNIFICANT BYTE FIRST!

;
;       Ken Staton
;       Feb 9, 1993     First coding
;       Apr 3, 1993     Added DAC support
;       		~44K of digitized speech: 
;			 A-Z = 26 + 0-9 = 10 + {.,?} = 3 total = 39
;			 {sk,bt,ar,dn,kn} = 5; 39 + 5 = 44
;			 could implement {sk,bt,ar,dn,kn} as 
;			 concatenations of letters
;       Jun 3, 1993     Added ADPCM digitized speech
;	Aug 17, 1993	Cleanup.  Removed Debug & RS232.
;	Aug 25, 1993	Changed 4Mc->3.2768Mc
;			Fixed interrupt problem (not re-entrant)
;			 by disabling INTS in speak & morse
;	Jan 25, 1994	Added BUILDIT speech mode in preparation
;			 for allophone based speech.  Currently
;			 used for concatenating sounds of prosigns.
;	Feb 1, 1994	Changed 3.2768Mc->6.0Mc to support 8KHz data rate.
;	Mar 19, 1994	Added SET Option. Commented out to disable.
;	Apr 16, 1994	Fixed char rate bug when changed from default.
;	Apr 20, 1994	Added programmed delay before and after speaking
;	Apr 22, 1994	Version 1.0
;
;---------------------------------------------------------------------
;
;	Select options using ext_int0
;	Options are:
;	Speed
;		5@16
;		13@18
;		20@23
;	Toggle
;		SPEECH
;	Random Chars
;	Random Groups of five chars
;	Sequence Chars
;	Toggle Set (see SUBSET; not enabled)
;		LETTERS
;		NUMBERS
;		PROSIGNS
;		PUNCTUATION
;**		PROGRESSIONS
;**		COMPLEMENTS
;	** = not yet implemented...
;
;



#INCLUDE "ISR_INIT.TXT"


;**************************************************************
;
; MAIN LOOP
;

MAIN
	CJNE	R4,#RAN,MODE0		;if R4=Random then JMP RCHARS
	LJMP	RCHARS

MODE0	CJNE	R4,#SEQ,MODE1		;if R4=Sequence then JMP CYCLE
	LJMP	CYCLE
	
MODE1	CJNE	R4,#GRP,MODE2		;if R4=Group then JMP RGRP
	LJMP	RGRP

MODE2	SJMP	MAIN

;**************************************************************
;
; RCHARS - Random Characters
;
	
RCHARS
	LCALL	RANDOM			; GENERATE RANDOM INDEX
TRUNC	MOV	A,PH
	RR	A
	RR	A
	RR	A
	MOV	COUNT,A			; SAVE INDEX
	CLR	C
	SUBB	A,#SETSIZE		; A <- A - C - #data
	JNC	RCHARS			; C=0 -> A>=SETSIZE
					; for INDEX: 0..SETSIZE-1
	MOV	A,COUNT
	LCALL	SUBSET
	JZ	RCHARS

	JNB	CODE,RCPO1
	MOV	A,COUNT			; restore index
	LCALL   MORSE			; sound code

RCPO1
	JNB	SPEAK,RCPO2
	MOV     A,COUNT	                ; restore index
	LCALL   SPEECH			; speak code

RCPO2
	LJMP	MAIN			; RESTART MAIN LOOP

;**************************************************************
;
; RGRP - Random Groups
;

RGRP
	PUSH	RH
	PUSH	RL

	LCALL	RCODE
	LCALL	RCODE
	LCALL	RCODE
	LCALL	RCODE
	LCALL	RCODE

	LCALL	WORD_DLY

	POP	RL		; Restore seed. psuedorandom,
	POP	RH		; so sequence will be the same...

	LCALL	RTALK
	LCALL	RTALK
	LCALL	RTALK
	LCALL	RTALK
	LCALL	RTALK

	LJMP	MAIN		; RESTART MAIN LOOP

;**************************************************************
;
; RCODE - Random Code SUBROUTINE for RGRP
;

RCODE	LCALL	RANDOM			; GENERATE RANDOM INDEX
	MOV	A,PH
	RR	A
	RR	A
	RR	A
	MOV	COUNT,A			; SAVE INDEX
	CLR	C
	SUBB	A,#SETSIZE
	JNC	RCODE			;C=0 -> A>=SETSIZE
					; for INDEX: 0..43
					; for INDEX: 0..SETSIZE-1
	MOV	A,COUNT
	LCALL	SUBSET
	JZ	RCODE

	MOV	A,COUNT			; RESTORE INDEX
	LCALL   MORSE			; sound code
	RET				; DONE

;**************************************************************
;
; RTALK - Random Talk SUBROUTINE for RGRP
;

RTALK	LCALL	RANDOM			; GENERATE RANDOM INDEX
	MOV	A,PH
	RR	A
	RR	A
	RR	A
	MOV	COUNT,A			; SAVE INDEX
	CLR	C
	SUBB	A,#SETSIZE
	JNC	RTALK			; C=0 -> A>=SETSIZE
					; for INDEX: 0..SETSIZE-1
	MOV	A,COUNT
	LCALL	SUBSET
	JZ	RTALK

	MOV	A,COUNT			; RESTORE INDEX
	JNB	SPEAK,RTDONE
	LCALL   SPEECH			; speak code

RTDONE
	RET				; DONE

;**************************************************************
;
; CYCLE - Cycle sequentially through character set
;

CYCLE
	MOV     COUNT,#0
NEXT_C  MOV     A,COUNT
	LCALL	SUBSET
	JZ	NEXT_C

	MOV	A,COUNT
	JNB	CODE,CYC1
	LCALL   MORSE			; sound code

CYC1
	JNB	SPEAK,CYC2		; test if speak on
	MOV     A,COUNT	                ; restore index
	LCALL   SPEECH			; speak code

CYC2
	INC     COUNT
	MOV	A,COUNT
	CJNE	A,#SETSIZE,NEXT_C
	LJMP	MAIN			; RESTART MAIN LOOP



;
;*************************************************************************
;
; RANDOM
;
; RANDOM NUMBERS BETWEEN 0 AND {26+10+3+4 = 43}-1 = 42
; i.e. mod 42
;
; Best bet is to generate seed by having 16 bit counter that
; increments until input at power up...this requires setting
; mode, or maybe just indicating when to start...
; 
; Some external asyncronous event must establish seed.
;   See EXT0 in INIT_ISR.
;
; Algorithm described in:
; The Art of Computer Programming Volume 2, Seminumerical Algorithms
;   Donald Knuth
;
; X <- (aX + c) mod m
;
; m = 65536
; c/m ~ 0.2113248654
; c odd
; 13.85 -> c = 13
; a > sqrt(m), a > m/100, a < m - sqrt(m)
; 256 < 655 < a < 65280
; a = 31415	{= 122*256 + 183}
;
; X <- (31415 * X + 13) mod 65536
;
; NOTE: that the arithmetic done in a 16 bit word
; is mod 2^16 = 65536
;
; A * B = (AL + 2^8 * AH) * (BL + 2^8 * BH)
;       = AL * BL + 2^8(AL * BH + AH * BL) + 2^16(AH * BH)
; A * 31415 = AL * 183 + 2^8(AL * 122 + AH * 183) + 2^16(AH * 122)
; 

RANDOM
	MOV	A,RL
	MOV	B,#183
	MUL	AB		; A[7..0],B[15..8] product bits
	MOV	PL,A
	MOV	PH,B

	MOV	A,RL
	MOV	B,#122
	MUL	AB
	ADD	A,PH
	MOV	PH,A

	MOV	A,RH
	MOV	B,#183
	MUL	AB
	ADD	A,PH
	MOV	PH,A

	CLR	C			; NOW ADD c
	MOV	A,PL
	ADDC	A,#13
	MOV	PL,A	
	JNC	NOCARRY
	INC	PH
NOCARRY
	MOV	RL,PL
	MOV	RH,PH	
	RET				; DONE RANDOM

;
;*************************************************************************
;
; Define code for MORSE SUBROUTINE
;
;
; numbers 5 bits
; punctuation 6 bits
; letters <= 4 bits
;
; b7  b6  b5    |   b[4:0] -> group
;---------------|--------------------------
;  0  0  d      |   number or prosign
;  0  1  d      |   punctuation or prosign
;  1  0  0      |   1 element letter
;  1  0  1      |   2 element letter
;  1  1  0      |   3 element letter
;  1  1  1      |   4 element letter
;		| 
; d => don't	| 1 => dah
;	care	| 0 => dit
; 

CODE_TBL

LETTERS
MCA .DB 10100001B
MCB .DB 11101000B
MCC .DB 11101010B
MCD .DB 11000100B
MCE .DB 10000000B
MCF .DB 11100010B
MCG .DB 11000110B
MCH .DB 11100000B
MCI .DB 10100000B
MCJ .DB 11100111B
MCK .DB 11000101B
MCL .DB 11100100B
MCM .DB 10100011B
MCN .DB 10100010B
MCO .DB	11000111B
MCP .DB 11100110B
MCQ .DB 11101101B
MCR .DB 11000010B
MCS .DB 11000000B
MCT .DB 10000001B
MCU .DB 11000001B
MCV .DB 11100001B
MCW .DB 11000011B
MCX .DB 11101001B
MCY .DB 11101011B
MCZ .DB 11101100B

NUMBERS
MC1 .DB 00001111B
MC2 .DB 00000111B
MC3 .DB 00000011B
MC4 .DB 00000001B
MC5 .DB 00000000B
MC6 .DB 00010000B
MC7 .DB 00011000B
MC8 .DB 00011100B
MC9 .DB 00011110B
MC0 .DB 00011111B

PUNCTUATION
MCPP .DB 01010101B
MCPC .DB 01110011B
MCPQ .DB 01001100B

PROSIGNS
MCSDN .DB 00010010B
MCSSK .DB 01000101B
MCSBT .DB 00010001B
MCSAR .DB 00001010B
MCSKN .DB 00010110B		; <<< NOT PART OF TEST SET!


SUBSET
;
; CHECKS IF CHARACTER INDEX IS IN AN ENABLED SUBSET
; RETURNS ACC=0 IF NOT ENABLED
; RETURNS ACC=1 IF ENABLED
;
;	CLR	C		
;	SUBB	A,#CONST	; C=0 -> A>=CONST
;
;	CLR	C
;	SUBB	A,#26		; A={0..25} -> LETTER
;	JC	IN_GL		; C=1 -> A<26
;	MOV	A,COUNT
;	SUBB	A,#36		; A={26..35} -> NUMBER	
;	JC	IN_GN		; C=1 -> 26<=A<36
;	MOV	A,COUNT
;	SUBB	A,#39		; A={36..38} -> PUNCTUATION
;	JC	IN_GP		; C=1 -> 36<=A<39
;	MOV	A,COUNT
;	SUBB	A,#43		; A={39..42} -> PRO-SIGN
;	JC	IN_GS		; C=1 -> 39<=A<43
;IN_GL
;	JNB	GL,NOT_IN_SET
;	MOV	A,#YES
;	RET
;IN_GN
;	JNB	GN,NOT_IN_SET
;	MOV	A,#YES
;	RET
;IN_GP
;	JNB	GP,NOT_IN_SET
;	MOV	A,#YES
;	RET
;IN_GS
;	JNB	GS,NOT_IN_SET
;	MOV	A,#YES
;	RET
;NOT_IN_SET
;	MOV	A,#NO
;	RET

	MOV	A,#YES		; always enabled for now...
	RET

SPEECH
;
; INPUT ACC = index for sound table, which must match
; 		order of code table


	CLR	C
	RLC	A		;MUL BY 2 for 2 byte address entries in table
	MOV	TMP2,A		;save index by 2
	MOV	DPTR,#SOUND_TBL

	MOVC	A,@A+DPTR
	MOV	ADDR_H,A	;save addrh

	MOV	A,TMP2		;restore index by 2
	INC	A		;get rest of addr
	MOVC	A,@A+DPTR
	MOV	DPL,A
	MOV	DPH,ADDR_H
;
; delay after code - before speaking
;
	MOV	R7,DLY_B4
	LCALL	DELAY
	MOV	R7,DLY_B4
	LCALL	DELAY
	MOV	R7,DLY_B4
	LCALL	DELAY
	MOV	R7,DLY_B4
	LCALL	DELAY

	MOV	TMP2,#0		;start at entry 0

BLD_LOOP	
	MOV	A,TMP2
	MOVC	A,@A+DPTR
	
	CJNE	A,#PA1,TP2	;test if 1mS pause
	SJMP	PAUSE1
TP2	CJNE	A,#PA10,TP3	;test if 10mS pause
	SJMP	PAUSE2
TP3	CJNE	A,#PA100,TP4	;test if 100mS pause
	SJMP	PAUSE3
TP4	CJNE	A,#PA300,TP5	;test if 500mS pause
	SJMP	PAUSE4
TP5	CJNE	A,#PA500,TTERM	;test if terminator (0FFH)
	SJMP	PAUSE5
TTERM	CJNE	A,#0FFH,BLD_MORE
;
; delay after speaking - before next code
;
	MOV	R7,DLY_AR
	LCALL	DELAY
	MOV	R7,DLY_AR
	LCALL	DELAY
	MOV	R7,DLY_AR
	LCALL	DELAY
	MOV	R7,DLY_AR
	LCALL	DELAY
	RET			; DONE


BLD_MORE
	LCALL	MAKESOUND
BMORE	INC	TMP2
	SJMP	BLD_LOOP


;
; These pauses are used in speech concatenation
;
	
PAUSE1
	MOV	R7,#1
	LCALL	DELAY
	SJMP	BMORE

PAUSE2
	MOV	R7,#10
	LCALL	DELAY
	SJMP	BMORE

PAUSE3
	MOV	R7,#100
	LCALL	DELAY
	SJMP	BMORE

PAUSE4
	MOV	R7,#150
	LCALL	DELAY
	MOV	R7,#150
	LCALL	DELAY
	SJMP	BMORE

PAUSE5
	MOV	R7,#250
	LCALL	DELAY
	MOV	R7,#250
	LCALL	DELAY
	SJMP	BMORE

SOUND_TBL
#INCLUDE "TABLE02.TXT"		; Defines contatenations

MAKESOUND
#INCLUDE "SPEAK.TXT"

MORSE
#INCLUDE "MORSE.TXT"


	.ORG	(0FFFFH-51)
	.TEXT	"COPYRIGHT (C) 1994 KEN STATON. ALL RIGHTS RESERVED"

	.ORG	0FFFFH
	.DB	0FFH
	.END
