>L. 100REM Speech Utilities 200REM Procedures & Functions for use with BBC Model B/B+ with Speech Upgrade 300REM Uses OSBYTE &9E and &9F to read and write from Speech Processor 400REM (C) Ian Wolstenholme 2023 500REM Version 1.1 26/III/2023 600REM Start to optimise ROL/ROR/REV calcs 700REM Done on Station 128 800REM phrom is Phrase ROM Number = 15 for Kenneth Kendall PhROM A 900REM start is location to start from in PhROM 1000REM ********************************************************************** 1100REM PROCload(P%,L%) - load address L% in PhROM P% into Speech Proc 1200REM FNread - read from Speech proc (Status byte unless address loaded) 1300REM FNreadbyte,FNread2bytes - read next byte/2 bytes from the PhROM 1400REM PROCspeak - speak from current PhROM pointer 1500REM PROCspeakfrom(P%,L%) - load L% in PhROM P% and speak from there 1600REM PROCreset - reset Speech Proc (stop any speech in progress) 1700REM FNrol(B%),FNror(B%) - rotate bits of B% left/right one bit 1800REM FNreverse(R$) - reverse R$ 1900REM FNreversebits(B%) - reverse bits in number B% 2000REM FNrotatestring(B$) - reverse B$ then rotate bits 2100REM FNsearch(S$,case,start,end) - locate string S$ in PhROM starting from 2200REM start and ending with end; also supports case-sensitive flage (unused) 2300REM PROCinfo(P%) - print information from header of PhROM P% 2400REM PROClistpointers(P%) - list all word pointers in PhROM P% 2500REM PROClistwords(P%) - extract all word-names in Phrom P% 2600REM FNfindword(S$)- find word S$ in word-list in PhROM 2700REM PROCsay(S$) - say S$ if found in word-list 2720REM PROCspeakword(N%) - say word number N% 2800REM ********************************************************************** 2900V%=FALSE:REM Verbose off 3000phrom=15:start=0:L%=0 3100W$=STRING$(16,CHR$0):W$="" 3200END 3300DEFPROCload(P%,L%) 3400A%=&9F:REM Write to speech Processor 3500REM Load address in speech processor; P%=PhROM, L%=address 3600IFV%PRINT"Loading &";~L%;" in Speech ROM Number &";P%;" into Speech Processor" 3700L%=P%*2^14+L%:REM Shift PhROM to bits 14-19 of 20 bit address 3800IFV%PRINT"Address to load into speech processor is &";~L% 3900IFV%PRINT"Sending address as 5 Hex Digits in reverse..."; 4000Y%=L% DIV&00000001 MOD&00000010 4100IFV%PRINT;~Y%; 4200Y%=Y%+&40:CALL&FFF4:REM Add Load Addr Command &40 and send 4300Y%=L% MOD&00000100 DIV&00000010 4400IFV%PRINT;~Y%; 4500Y%=Y%+&40:CALL&FFF4:REM 2nd digit 4600Y%=L% DIV&00000100 MOD&00000010 4700IFV%PRINT;~Y%; 4800Y%=Y%+&40:CALL&FFF4:REM 3rd digit 4900Y%=L% MOD&00010000 DIV&00001000 5000IFV%PRINT;~Y%; 5100Y%=Y%+&40:CALL&FFF4:REM 4th digit 5200Y%=L% DIV&00010000 MOD&00000010 5300IFV%PRINT;~Y%; 5400Y%=Y%+&40:CALL&FFF4:REM 5th digit 5500IFV%PRINT 5600ENDPROC 5700DEFFNread 5800REM Returns a byte read from speech processor (in Y register) 5900REM N.B. This will return the Status byte unless the Read Command has been 6000REM written to the Speech Processor immediately before 6100A%=&9E:REM Read from speech processor 6200=(USR&FFF4 AND &FF0000)DIV&10000 6300DEFFNreadbyte 6400A%=&9F:Y%=&10:CALL&FFF4:REM Send read byte command 6500=FNread 6600DEFPROCspeak 6700A%=&9F:Y%=&50:CALL&FFF4:REM Send speak command; speaks from current PhROM pointer 6800ENDPROC 6900DEFPROCspeakfrom(P%,L%) 7000PROCload(P%,L%):PROCspeak 7100ENDPROC 7200DEFPROCreset 7300A%=&9F:Y%=&70:CALL&FFF4:REM Send reset command 7400ENDPROC 7500DEFFNrol(B%) 7600=B%MOD32768*2+B%DIV32768 7700DEFFNror(B%) 7800=(B%DIV2)+(B%MOD2)*32768 7900DEFFNreverse(R$) 8000S$=R$:S$="" 8100FORB%=1TOLENR$ 8200S$=MID$(R$,B%,1)+S$ 8300NEXT 8400=S$ 8500DEFFNrotatestring(F$) 8600REM Reverse, then Rotate left each byte of string by one bit: 8700REM Words are stored in reverse order with each byte rotated right 1 bit! 8800G$=FNreverse(F$) 8900IFV% PRINT"Word ";F$;" reversed to ";G$ 9000H$=G$:H$="" 9100FOR B%=1TO LENG$ 9200H$=H$+CHR$(FNrol(ASC(MID$(G$,B%,1)))) 9300NEXT 9400IFV%PRINT"Rotated word is ";:FORB%=1TO LEN H$:PRINT;~ASC(MID$(H$,B%,1))" ";:NEXT 9500IFV%PRINT 9600=H$ 9700DEFFNsearch(search$,case,start,end) 9800len=end-start:IFlen<1 OR lenstrlen C%=1:E%=TRUE 11700IFE% ANDV% PRINT"Found at &";~D%+start;" " 11800B%=B%+1 11900UNTIL B%>len OR E% 12000IFD%=-1 PRINT 12100IFE% THEN =start+D% ELSE =0 12200DEFPROCinfo(P%) 12300PRINT'"Information about Phrase ROM ";P% 12400PROCload(P%,2):IF FNreadbyte=ASC"(" AND FNreadbyte=ASC"C" AND FNreadbyte=ASC")" ELSE PRINT"Not a Phrase ROM":ENDPROC 12500PROCload(P%,0) 12600PRINT"Phrom Format Type: ";FNreadbyte 12700PRINT"File data flag: ";FNreadbyte 12800B%=0:C$="" 12900REPEAT 13000C%=FNreadbyte 13100C$=C$+CHR$C% 13200B%=B%+1 13300UNTILB%=52 13400E%=INSTR(C$,CHR$0):F%=INSTR(MID$(C$,E%+1),CHR$0)+E% 13500G%=INSTR(MID$(C$,F%+1),CHR$0)+F% 13600PRINT"Copyright String: ";LEFT$(C$,E%) 13700PRINT"Title String: ";MID$(C$,E%,F%-E%) 13800PRINT"Version String: ";MID$(C$,F%,G%-F%) 13900C%=FNread2bytes 14000PRINT"PhROM Serial Number: ";C% 14100C%=FNread2bytes 14200PRINT"Number of ASCII Words:" ;C% 14300W%=FNread2bytes 14400PRINT"Number of Word Pointers: ";W% 14500C%=FNread2bytes 14600PRINT"End of ROM Data pointer: &";~C% 14700C%=FNread2bytes 14800PRINT"End of Speech Data pointer: &";~C% 14900ENDPROC 15000DEFPROClistpointers(P%) 15100PRINT"List Word Pointers in PhROM ";P% 15200PROCload(P%,2):IF FNreadbyte=ASC"(" AND FNreadbyte=ASC"C" AND FNreadbyte=ASC")" ELSE PRINT"Not a Phrase ROM":ENDPROC 15300PROCload(P%,56):U%=FNread2bytes:REM ASCII Words 15400W%=FNread2bytes:REM Word pointers 15500PROCload(P%,64):REM Move to first pointer 15600PRINT"Word Pointers:" 15700FORC%=1TOW% 15800D%=FNreadbyte*256+FNreadbyte 15900PRINT"Word ";C%+(31 AND U%);" Pointer: &";~FNreversebits(D%) 16000NEXT 16100IFFNreadbyte=255 ELSE PRINT"Error with end of pointers marker" 16200ENDPROC 16300DEFPROClistwords(P%) 16400LOCAL B%,C%,D%,E%,F%,S% 16500PRINT"List Word Names in PhROM ";P% 16600PROCload(P%,2):IF FNreadbyte=ASC"(" AND FNreadbyte=ASC"C" AND FNreadbyte=ASC")" ELSE PRINT"Not a Phrase ROM":ENDPROC 16700PROCload(P%,56):U%=FNread2bytes:REM ASCII pointers 16800W%=FNread2bytes:REM Word pointers 16900IFU%=0 THEN T%=64 ELSE T%=254:REM Work out earliest word pointer - if no ASCII then it will be at 32*2, or if ASCII then it will be at 127*2 17000PROCload(15,T%):S%=FNreversebits(FNreadbyte*256+FNreadbyte) 17100PROCload(P%,W%*2+64):REM Should be FF byte after end of pointer list 17200IFFNreadbyte=255 ELSE PRINT"Error with end of pointers marker" 17300R%=W%*2+65:REM THIS IS CURRENT POINTER AFTER LAST READ - THIS IS THE WORD NAME BEFORE THE FIRST WORD STARTING AT THE VALUE IN S% 17400REM THEREFORE THE LENGTH OF THE WORD NAME IS S%-R% 17500E%=R%:REM From now on, E% is the current PhROM pointer 17600W$="" 17700PRINT"Word name ";1+(126 AND U%<>0);": "; 17800FORC%=1TOS%-R% 17900E%=E%+1 18000W$=CHR$(FNror(FNreadbyte))+W$ 18100NEXT 18200PRINTW$ 18300REM Next do all other words; pointer is currently at beginning of speech data for first word 18400FORC%=1TOW%-U%-1 18500PRINT"Word name ";C%+(127 AND U%<>0);": "; 18600PROCload(P%,C%*2+T%):H%=FNreversebits(FNreadbyte*256+FNreadbyte) 18700REM H% is now start of next word speech data 18800PROCload(P%,E%):REM Restore pointer 18900REPEATE%=E%+1:I%=FNreadbyte 19000IF(I% AND &F)=&F THEN J%=E% 19100UNTILE%=H%:REM J% NOW HAS LOCATION OF STOP CODE (1111) IMMEDIATELY BEFORE THE NEXT WORD POINTER, THEREFORE POINTS TO THE START OF THE WORD NAME 19200E%=J%:PROCload(P%,E%):REM Restore pointer to beginning of word name 19300W$="" 19400FORF%=1TOH%-E% 19500E%=E%+1 19600W$=CHR$(FNror(FNreadbyte))+W$ 19700NEXT 19800PRINTW$ 19900NEXT 20000ENDPROC 20100DEFFNfindword(S$) 20200=FNsearch(FNrotatestring(S$),0,&40,&4000) 20300DEFPROCsay(S$) 20400IFFNfindword(S$)PROCspeak ELSEPRINT"Word not found" 20500ENDPROC 20600DEFFNreversebits(M%) 20700N%=0 20800FORQ%=0TO15 20900IFM% AND 2^Q% THEN N%=N%+2^(15-Q%) 21000NEXT 21100=N% 21200DEFFNread2bytes 21300=FNreadbyte+(FNreadbyte*256) 21400DEFPROCspeakword(N%) 21500PROCload(phrom,N%*2) 21600PROCload(phrom,FNreversebits(FNreadbyte*256+FNreadbyte)) 21700PROCspeak 21800ENDPROC 22000DEFPROCsentence 22010REPEAT 22020READword%,len% 22030PROCspeakword(word%) 22040IFlen%>0 THEN T%=TIME:REPEATUNTILTIME>T%+len%:PROCreset 22050UNTILword%=-1 22055ENDPROC 22060DATA219,35,179,0,209,0,160,37,133,0,-1,-1 22070DATA73,0,163,18,269,38,134,0,137,0,-1,-1 >*SPOOL