' ' Story Teller, Model 31 vocalizer, PICSTIC code ' Tom Jennings ' $Id$ ' ' Receives allophone opcodes from a papertape reader via serial ' interface, and and drives a General Instrument SPO256-AL2 speech ' chip. Opcodes are partially decoded and drive front-panel lamps. ' Data is encoded in our local standard format, record type P. ' ' mru 17 Apr 2003 ' Changed to modern WPS state machine, modern serial ' routines swiped from M423. Removed all the "DEMO" ' functions. ' ' mru 22 Apr 2002 ' Made PAUSE and LONGPAUSE blink the appropriate lamp, since the ' whole system looks locked up otherwise. ' ' mru 22 Aug 1999 ' Sigh, SYN is reserved, changed DELAY to EM, added SUB as LONGDELAY. ' ' mru 18 Aug 1999 ' Added SYN LONGDELAY opcode. ' ' mru 9 Mar 1999 ' Changed to 9600 baud -- and back to 2400, when the Model 02 didn't work. ' ' mru 25 Feb 1999 ' Repaired STX/ETX code. ' ' mru 17 Feb 1999 ' Made comply with WPS file format type P. ' ' Control codes: ' DC2, 18 dec demo mode: after EOT or NUL is reached, a random ' delay of 1 - 30 seconds before the next record ' is read (accomodates tape loops) ' DC4, 20 dec demo mode off. ' EM, 25 dec Pauses two seconds. Does not affect opcodes in the ' speech chip; if used as a long silence a PAx opcode ' should be issued first. ' SUB, 26 dec Pauses fifteen seconds. Does not affect opcodes in the ' speech chip; if used as a long silence a PAx opcode ' should be issued first. ' ' new 1 Jan 1999 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' file: c:\tomj\projects\model 01, CPU-IO card\model01.bas '' General purpose definitions and functions for the Model 01 '' CPU I/O card. '' '' '' Tom Jennings '' mru 2 Aug 98 '' written 17 July 98 '' '' For unrevised Model 01 card (prototype) card. '' '' Copyright Tom Jennings, 1998. '' symbol demo= bit0 ' 1 == demo mode symbol indata= bit1 ' 1 == between STX, ETX chars '' '' The shift registers need data shifted to them MS bit first; the MS bit '' of the value to be written is isolated by using the byte/word organization '' of the PIC; the value to be shifted is placed in the LS byte, and added '' to itself to shift left; the MS bit of the LS byte is thence shifted into '' the LS bit of the MS byte. Easy, no? '' symbol k= b2 ' val stored in K symbol c= b3 symbol n= b4 ' general purpose regs, used here symbol j= b5 symbol l= b6 symbol b= b7 ' boot-up message index symbol speedpot= b8 ' SPEED pot reading, 0= normal speed symbol opcode= b9 ' allophone opcode read symbol lastlamp= b10 ' last lamp value, symbol serflag= b11 '' '' These definitions are hard-wired on the PCB. Data is written to the shift '' registers by asserting data on the SD pin, and clocking the appropriate '' SR clock pin. 8 bits per shift register, MS bit output first. '' ' PICSTIC I/O pin assignments symbol CTSpin= pin7 ' CTS (to PTR RS-232) symbol CTS= 7 symbol ALDpin= pin6 ' /ADL (SPO256-AL2) symbol ALD= 6 symbol RXDpin= pin5 ' RxD (from PTR RS-232) symbol RXD= 5 symbol POTpin= pin4 ' speed pot symbol POTp=4 ' speed pot pin symbol SRC= 3 ' SR C clock, opcode SPO256-AL2 ' NOTE: Previous code waited for SBY; we now use LQR. Requires ' rewire to change. symbol SBYpin= pin2 ' /SBY (SPO256-AL2) symbol LRQpin= pin2 ' /LRQ (SPO256-AL2) symbol SRB= 2 ' SR B clock, NOT USED symbol SRA= 1 ' SR A clock, 8 bits of lamps symbol SD= 0 symbol SDpin= pin0 ' Model 01, SR data ' DSR (from PTR RS-232) is on PA4 symbol SPEED= N2400 symbol NUL= 0 ' paper tape NUL symbol SOH= 1 ' start of header symbol STX= 2 ' start of text (data) symbol ETX= 3 ' end of text (data) symbol TYPE= 80 ' 'P' symbol EOT= 4 ' end of text, symbol DC2= 18 ' start demo mode symbol DC4= 20 ' end demo mode symbol EM= 25 ' PAUSE opcode symbol SUB= 26 ' LONGPAUSE opcode symbol PA1= 32 ' 10mS silence, ASCII bias ' ' Phoneme-type encoding, for lighting lamps. From the RADIO SHACK data ' sheet, types merged to be fewer categories. ' symbol SI= 1 ' silence symbol VO= 2 ' vowel (long, short, R-colored) symbol ST= 4 ' stop (voiced, unvoiced) symbol FR= 8 ' fricative (voiced, unvoiced) symbol RE= 16 ' resonant symbol AF= 32 ' affricate symbol NA= 64 ' nasal dirs= %11001001 ' set directions (1=output), pins= %11000000 ' set initial states (low=true) opcode= PA1 : call outallo ' output silence, peek 133, n : n= n | 16 : poke 133, n ' make PA4 input (RS-232 DSR) ' These only need to be compiled in if EEPROM contents change, or the PICSTIC ' is changed. eeprom (0, "$$;'MU#",34,"$$#", 0) ' ("HELLO.") eeprom ("7&#",34,":0#",34,"S'AA3#", 34,"$$#", 0) ' ("I AM READY") pause 800 ' let audio amp shut up, b= 0 : gosub speek ' say hello first, ' Pretty lamp test. k= 0 : call outA ' all lamps off, pause 200 for j= 1 to 4 ' LSB alternates per iteration b= j & 1 ' b= 1, 0, 1, 0 ... k= 0 ' initial lamps (all off) for n= 1 to 8 k= k + k + b ' lamps change on the right, call outA pause 75 next n next j gosub speek ' say rest of canned speech. ' ==================================================================== ' ' WPS Protocol state machine. ' call serini ' init serial input. ' Ground state: return here on error or EOT. s0: opcode= PA1 : call outallo ' output silence & lamp, ' Await SOH. s1: call serinw : if c <> SOH then s1 ' wait for SOH, ' Await . s2: call serinw ' await character, if c <> "P" then s0 ' wait til our type, ' Await STX. s4: indata= 0 ' await STX ("start of data") ' s5 handles the STX data... ETX sequence. ' NUL protocol failure, restart ' SOH nonsense, ignore ' STX data follows, eaten ' ETX end of data, ' EOT end of block, restart ' all data passed to handler s5: call serinw ' process char stream, branch c, (s0, s5, s6, s4, s0) ' 0:eh 1:eh 2:STX 3:ETX 4:EOT ' Falling through the branch means it's not a stream-control character; ' if indata is true, then it's payload data. If it's false, then ' we're waiting for STX. if indata = 0 then s5 ' ignore data before STX gosub spk ' do actual work, goto s5 ' STX: data follows this character until ETX (or error). s6: indata= 1 : goto s5 ' data will follow, ' ==================================================================== ' Utter the opcode just received. We achieve some paltry I/O ' overlap by waiting for the device to stop speaking, issuing ' the new opcode and returning before it's done. Since phonemes ' take 10 - 80mS to complete, we'll do some decent overlap. ' Opcodes have ASCII bias, eg. 32 and up. Control codes ' do special things. spk: if opcode > 31 then sk1 ' if a control code, ' if opcode = DC2 then sk3 ' look for DEMO ON, ' if opcode = DC4 then sk4 ' and DEMO OFF, n= 2 : if opcode = EM then sdelay ' and PAUSE, n= 15 : if opcode = SUB then sdelay ' and LONGPAUSE, return ' Before we output the opcode, extend the utterance of the ' current phoneme by the last reading from the SPEED pot. ' We only read the SPEED pot on PAUSEx opcodes, since the ' pot command is slow. sk1: gosub outallo ' output the allophone, if opcode > 36 then sk2 ' if PAx opcode (PAUSE), pot POTp, 255, speedpot ' get new speed value, sk2: pause speedpot ' output speed, return ' ' PAUSE for N seconds; this simply leaves the last-issued opcode ' onto the chip for the specified period. For use as a pause PAx ' should be issued first. The appropriate phoneme lamp is ' flashed so the system doesn't look dead! ' sdelay: pause 500 ' 1/2 sec lamp on, k= 0 : call outA ' all lamps off, pause 500 ' for 1/2 second, k= lastlamp : call outA ' restore lamps, n= n - 1 : if n > 0 then sdelay ' repeat... ret: return ' When the chip is ready, output the allophone encoded in 'opcode' ' and light the right lamp that indicates the allophone type. ' We wait for /LRQ (buffer empty) the output the allophone. (Previous ' code waited for /SBY (chip finished speaking) to ensure sync with ' the lamps, but also waited for /SBY after issuing the allophone, ' which prevented all I/O overlap.) outallo: if LRQpin = 0 then outallo ' wait for not speaking, n= opcode - 32 ' remove ASCII bias, lookup n, (SI, SI, SI, SI, SI, VO, VO, VO, ST, ST, AF, NA, VO, ST, RE, VO, NA, VO, FR, VO, VO, ST, VO, VO, VO, RE, VO, FR, ST, FR, VO, VO, VO, ST, ST, FR, ST, FR, FR, RE, FR, ST, ST, FR, NA, RE, RE, VO, FR, RE, AF, VO, VO, VO, FR, FR, NA, FR, VO, VO, VO, ST, RE, ST), lastlamp ' lamp select, k= lastlamp : call outC ' light the lamp, k= n : call outA ' output allophone, pulsout ALD, 1 ' strobe ALD, return ' ' Speak canned text from EEPROM, starting at location B, until we reach a 0. ' speek: b= b + 1 read b, opcode ' read data, if opcode = 0 then ret ' until EOF marker, gosub outallo ' speak it, goto speek ' repeat. end asm ; ; Serial input routines. These build upon the library routines that ; come with the compiler. All routines deassert CTS when a character ; is received (since we have no buffer). ; ; Assert CTS, wait for a character. _serinw call _serinp ; assert CTS, check for char, btfss _serflag, 0 ; if serflag LSB = 0, goto _serinw ; keep waiting. return ; Assert CTS, check for a character by seeing if a start ; bit is present; if so, drop CTS, receive the character ; into C, set serflag. _serinp bcf _serflag, 0 ; clear serflag, bsf portB, _CTS ; assert CTS, btfss portB, _RXD ; if Rx bit false goto done ; return, no char avail. call serin@W ; else read the char, bcf portB, _CTS ; deassert CTS, movwf _C ; store char in C, bsf _serflag, 0 ; set serflag= 1. goto done ; Initialize the serial input routine; pass the Rx pin to the ; PIC library routine, deassert CTS, clear our flag and return. _serini bcf portB, _CTS ; deassert CTS, movlw _RXD ; Rx pin number, call BP@Pin movlw _SPEED ; port speed, movwf GOP ; (local to ass'y code?) clrf _serflag ; set serflag to zero. goto done ; ; Output N to port A. ; Modifies K, J. ; _outA movlw 8 movwf _J ; set bit loop counter, _oa1 bcf portB, _SD ; set data bit= 0 rlf _K ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRA ; clock data bit out, bcf portB, _SRA decfsz _J ; repeat. goto _oa1 goto done ; ; Output N to port B. ; Modifies K, J. ; _outB movlw 8 movwf _J ; set bit loop counter, _ob1 bcf portB, _SD ; set data bit= 0 rlf _K ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRB ; clock data bit out, bcf portB, _SRB decfsz _J ; repeat. goto _ob1 goto done ; ; Output N to port C. ; Modifies K, J. ; _outC movlw 8 movwf _J ; set bit loop counter, _oc1 bcf portB, _SD ; set data bit= 0 rlf _K ; move MSB to carry, btfsc status, 0 ; if carry set (MSB == 1) bsf portB, _SD ; assert a 1, bsf portB, _SRC ; clock data bit out, bcf portB, _SRC decfsz _J ; repeat. goto _oc1 goto done ; ; Initialize PA3 as an output, PA4 as an input. ; _PAinit movf 133, w ; read (WHAT)? iorlw 16 ; PA3 is input, andlw 247 ; PA4 is output, movwf 133 ; write reg. return ; ; Set PA3 on and off. ; _PA3lo movf 5, w ; read PA andlw 247 ; clear PA3 movwf 5 return _PA3hi movf 5, w ; read PA iorlw 8 ; set PA3 movwf 5 return ; ; Return in N the state of PA4 (0 or 1). ; _PA4in movf 5, w ; read PA, andlw 16 ; mask it off, movwf _N ; store in N, rrf _N rrf _N rrf _N rrf _N ; shift to bit 0 return endasm