| Currah μSpeech | Routines |
| Prev: 0000 | Up: Map | Next: 01AB |
|
Any access to HandleInterrupt (including this opcode fetch) pages the μSpeech ROM in or out. The Spectrum IM1 handler lands here, so this ROM runs first. Every exit jumps back to HandleInterrupt, which pages the Spectrum ROM in and lets IM1 finish.
|
||||
| HandleInterrupt | 0038 | PUSH AF | Keep AF unchanged. | |
| 0039 | POP AF | |||
| 003A | DI | Disable interrupts. | ||
| 003B | PUSH HL | Stash HL, AF, BC and DE on the stack. | ||
| 003C | PUSH AF | |||
| 003D | PUSH BC | |||
| 003E | PUSH DE | |||
|
The speech workspace is a short block just below *RAMTOP (low byte 57). A 48K machine uses FF57; a 16K machine uses 7F57.
|
||||
| 003F | LD HL,($5CB2) | Fetch *RAMTOP. | ||
| 0042 | LD A,L | Jump to ProbeWorkspace if the low byte is not 57. | ||
| 0043 | CP $57 | |||
| 0045 | JR NZ,ProbeWorkspace | |||
| 0047 | LD A,H | Jump to InstallWorkspace if this is a 48K machine. | ||
| 0048 | CP $FF | |||
| 004A | JR Z,InstallWorkspace | |||
| 004C | CP $7F | Jump to InstallWorkspace if this is a 16K machine. | ||
| 004E | JR Z,InstallWorkspace | |||
|
*RAMTOP is not the installed workspace yet.
|
||||
| ProbeWorkspace | 0050 | LD A,$06 | Call ReadWorkspace for the saved buffer pointer. | |
| 0052 | CALL ReadWorkspace | |||
| 0055 | LD B,H | Copy the workspace address to BC. | ||
| 0056 | LD C,L | |||
| 0057 | CALL CheckMemory | Call CheckMemory and jump to InstallWorkspace if the reserved block is already below *RAMTOP. | ||
| 005A | JR C,InstallWorkspace | |||
| 005C | CALL ReadBufferPointer | Call ReadBufferPointer. | ||
| 005F | CALL CheckMemory | Call CheckMemory. | ||
| 0062 | CALL C,ResetBufferPointer | Call ResetBufferPointer if the pointer is out of range. | ||
| 0065 | XOR A | Call ReadWorkspace for the flags byte. | ||
| 0066 | CALL ReadWorkspace | |||
| 0069 | BIT 2,A | Jump to ServiceSpeech if the workspace is already live. | ||
| 006B | JR NZ,ServiceSpeech | |||
| 006D | SET 2,(HL) | Mark the workspace as being installed. | ||
| 006F | POP DE | Restore DE, BC, AF and HL from the stack. | ||
| 0070 | POP BC | |||
| 0071 | POP AF | |||
| 0072 | POP HL | |||
|
Page the Spectrum ROM back in so IM1 can run.
|
||||
| ReturnToSpectrum | 0073 | PUSH HL | No operation. | |
| 0074 | PUSH HL | |||
| 0075 | POP HL | |||
| 0076 | POP HL | |||
| 0077 | LD HL,$0038 | Set the return address to HandleInterrupt. | ||
| 007A | PUSH HL | Stash the return address on the stack. | ||
| 007B | DEC SP | Move SP down two bytes. | ||
| 007C | DEC SP | |||
| 007D | POP HL | Restore HL from the stack. | ||
| 007E | JP HandleInterrupt | Jump to HandleInterrupt. | ||
|
Wait a few frames, then reserve a page below *RAMTOP for the speech workspace.
|
||||
| InstallWorkspace | 0081 | LD A,($5C78) | Jump to ExitInterrupt if *FRAMES is still less than 05. | |
| 0084 | CP $05 | |||
| 0086 | JP C,ExitInterrupt | |||
| 0089 | XOR A | Call ReadWorkspace for the flags byte. | ||
| 008A | CALL ReadWorkspace | |||
| 008D | DEC H | Lower *RAMTOP by one page. | ||
| 008E | LD ($5CB2),HL | |||
| 0091 | INC H | Write 82 to the flags byte. | ||
| 0092 | LD (HL),$82 | |||
| 0094 | DEC HL | Clear workspace index 01. | ||
| 0095 | XOR A | |||
| 0096 | LD (HL),A | |||
| 0097 | DEC HL | Store the workspace page in index 02. | ||
| 0098 | LD (HL),H | |||
| 0099 | DEC HL | Write 51 to the buffer pointer (index 03). | ||
| 009A | LD (HL),$51 | |||
| 009C | DEC HL | Clear workspace indexes 04 and 05. | ||
| 009D | LD (HL),A | |||
| 009E | DEC HL | |||
| 009F | LD (HL),A | |||
| 00A0 | LD HL,($5CB2) | Fetch the new *RAMTOP. | ||
|
Plant a sentinel and move the machine stack below the new *RAMTOP.
|
||||
| InstallWorkspace_Stack | 00A3 | LD (HL),$3E | Write a 3E sentinel at the new *RAMTOP. | |
| 00A5 | DEC HL | |||
| 00A6 | LD (HL),A | Write 00 beneath it. | ||
| 00A7 | LD SP,HL | Point SP at the new *RAMTOP. | ||
| 00A8 | DEC HL | Clear the two bytes below the new stack. | ||
| 00A9 | LD (HL),A | |||
| 00AA | DEC HL | |||
| 00AB | LD (HL),A | |||
| 00AC | LD ($5C3D),HL | Write the error stack to *ERR_SP. | ||
| 00AF | LD DE,$12A9 | Set the return address to MAIN-5. | ||
| 00B2 | PUSH DE | Stash the return address on the stack. | ||
| 00B3 | EI | Enable interrupts. | ||
| 00B4 | JP HandleInterrupt | Jump to HandleInterrupt. | ||
|
The workspace is live. Draw the banner once, then speak.
|
||||
| ServiceSpeech | 00B7 | RES 2,(HL) | Clear the "installing" flag. | |
| 00B9 | BIT 7,(HL) | Call PrintBanner if the banner has not been drawn yet. | ||
| 00BB | CALL NZ,PrintBanner | |||
|
Keep *FRAMES moving while this ROM owns the interrupt.
|
||||
| 00BE | LD HL,($5C78) | Fetch the *FRAMES word. | ||
| 00C1 | LD A,H | Jump to ServiceSpeech_Tick if *FRAMES is non-zero. | ||
| 00C2 | OR L | |||
| 00C3 | JR NZ,ServiceSpeech_Tick | |||
| 00C5 | LD HL,$5C7A | Decrease the third *FRAMES byte. | ||
| 00C8 | DEC (HL) | |||
| ServiceSpeech_Tick | 00C9 | LD HL,($5C78) | Decrease the *FRAMES word. | |
| 00CC | DEC HL | |||
| 00CD | LD ($5C78),HL | |||
|
Tick the two keyboard-state timer pairs.
|
||||
| 00D0 | LD HL,$5C02 | Point at the keyboard-state timers (04 bytes apart). | ||
| 00D3 | LD DE,$0004 | |||
| 00D6 | CALL IncrementPair | Call IncrementPair. | ||
| 00D9 | DEC HL | Move back one byte. | ||
| 00DA | CALL IncrementPair | Call IncrementPair. | ||
|
Programs speak by assigning the BASIC string s$. Look it up and expand it into allophones if it exists.
|
||||
| 00DD | LD A,$53 | Call FindVariable for "s$". | ||
| 00DF | CALL FindVariable | |||
| 00E2 | CALL Z,ParseSpeechString | Call ParseSpeechString if s$ exists. | ||
| 00E5 | CALL TestKeysVariable | Call TestKeysVariable. | ||
| 00E8 | JP Z,FeedAllophone | Jump to FeedAllophone if key speech is enabled. | ||
| 00EB | XOR A | Call ReadWorkspace for the flags byte. | ||
| 00EC | CALL ReadWorkspace | |||
| 00EF | LD A,($5C3B) | Fetch *FLAGS. | ||
| 00F2 | BIT 5,A | Jump to ServiceSpeech_NoKey if no new key was pressed. | ||
| 00F4 | JR Z,ServiceSpeech_NoKey | |||
| 00F6 | BIT 4,(HL) | Jump to FeedAllophone if this key is already being spoken. | ||
| 00F8 | JR NZ,FeedAllophone | |||
| 00FA | JR ServiceSpeech_Key | Jump to ServiceSpeech_Key. | ||
| ServiceSpeech_NoKey | 00FC | RES 4,(HL) | Clear the "key held" flag and jump to FeedAllophone. | |
| 00FE | JR FeedAllophone | |||
|
A new key is in *LAST_K; turn it into allophones.
|
||||
| ServiceSpeech_Key | 0100 | SET 4,(HL) | Set the "key held" flag. | |
| 0102 | LD A,($5C08) | Fetch *LAST_K. | ||
| 0105 | CP $7B | Jump to ServiceSpeech_NotLetter if the code is 7B or higher. | ||
| 0107 | JR NC,ServiceSpeech_NotLetter | |||
| 0109 | CP $61 | Jump to ServiceSpeech_NotLetter if the code is below "a". | ||
| 010B | JR C,ServiceSpeech_NotLetter | |||
| 010D | SUB $20 | Convert a lowercase letter to uppercase and jump to ServiceSpeech_Lookup. | ||
| 010F | JR ServiceSpeech_Lookup | |||
|
Not a letter. Block graphics share key 80; other symbols go through PunctuationKeys.
|
||||
| ServiceSpeech_NotLetter | 0111 | CP $A5 | Jump to ServiceSpeech_Punctuation if the code is A5 or higher. | |
| 0113 | JR NC,ServiceSpeech_Punctuation | |||
| 0115 | CP $80 | Jump to ServiceSpeech_Punctuation if the code is below 80. | ||
| 0117 | JR C,ServiceSpeech_Punctuation | |||
| 0119 | LD A,$80 | Use dictionary key 80 and jump to ServiceSpeech_Lookup. | ||
| 011B | JR ServiceSpeech_Lookup | |||
| ServiceSpeech_Punctuation | 011D | LD HL,$042D | Search PunctuationKeys for this character. | |
| 0120 | LD BC,$000D | |||
| 0123 | CPIR | Jump to ServiceSpeech_Lookup if there is no mapping. | ||
| 0125 | JR NZ,ServiceSpeech_Lookup | |||
| 0127 | LD BC,$000C | Fetch the replacement from PunctuationValues. | ||
| 012A | ADD HL,BC | |||
| 012B | LD A,(HL) | |||
|
Look the key up in AllophoneDictionary.
|
||||
| ServiceSpeech_Lookup | 012C | LD HL,$0447 | Point at AllophoneDictionary. | |
| 012F | LD B,A | Keep the key in B. | ||
| ServiceSpeech_FindKey | 0130 | BIT 7,(HL) | Jump to ServiceSpeech_NextKey if this byte ends an allophone list. | |
| 0132 | JR NZ,ServiceSpeech_NextKey | |||
| 0134 | INC HL | Move to the next dictionary byte. | ||
| 0135 | JR ServiceSpeech_FindKey | Jump to ServiceSpeech_FindKey. | ||
| ServiceSpeech_NextKey | 0137 | INC HL | Move to the next dictionary key. | |
| 0138 | XOR A | Jump to FeedAllophone if the table has ended. | ||
| 0139 | CP (HL) | |||
| 013A | JR Z,FeedAllophone | |||
| 013C | LD A,B | Jump to ServiceSpeech_Copy if this key matches. | ||
| 013D | CP (HL) | |||
| 013E | JR Z,ServiceSpeech_Copy | |||
| 0140 | INC HL | Move to the next dictionary byte. | ||
| 0141 | JR ServiceSpeech_FindKey | Jump to ServiceSpeech_FindKey. | ||
|
Copy the matching allophones into the speech buffer.
|
||||
| ServiceSpeech_Copy | 0143 | INC HL | Move to the first allophone of this entry. | |
| 0144 | CALL CompareBuffer | Call CompareBuffer and jump to ServiceSpeech_Store if the buffer pointers already match. | ||
| 0147 | JR Z,ServiceSpeech_Store | |||
| 0149 | INC BC | Step the buffer pointer on one byte. | ||
| 014A | LD A,(BC) | Jump to FeedAllophone if the previous allophone is not a terminator. | ||
| 014B | RLA | |||
| 014C | JR NC,FeedAllophone | |||
| 014E | CALL ResetBufferPointer | Call ResetBufferPointer. | ||
| ServiceSpeech_Store | 0151 | LD A,(HL) | Queue this allophone in the speech buffer. | |
| 0152 | LD (BC),A | |||
| 0153 | DEC BC | |||
| 0154 | CALL CheckMemory | Call CheckMemory and jump to ServiceSpeech_Flush if the buffer has hit *RAMTOP. | ||
| 0157 | JR C,ServiceSpeech_Flush | |||
| 0159 | BIT 7,(HL) | Jump to ServiceSpeech_Commit if this was the last allophone of the word. | ||
| 015B | JR NZ,ServiceSpeech_Commit | |||
| 015D | INC HL | Move to the next allophone. | ||
| 015E | JR ServiceSpeech_Store | Jump to ServiceSpeech_Store. | ||
| ServiceSpeech_Flush | 0160 | CALL ReadBufferPointer | Call ReadBufferPointer. | |
| ServiceSpeech_Commit | 0163 | CALL WriteBufferPointer | Call WriteBufferPointer. | |
|
The SP0256 is the speech-synthesiser chip. Read its busy flag at 1000 and send the next queued allophone if it is idle.
|
||||
| FeedAllophone | 0166 | LD A,($1000) | Read the SP0256 status from 1000. | |
| 0169 | BIT 0,A | Jump to ExitInterrupt if the chip is still busy. | ||
| 016B | JR NZ,ExitInterrupt | |||
| 016D | LD A,($5C3A) | Fetch *ERR_NR. | ||
| 0170 | RLA | Call ResetBufferPointer if BASIC is not reporting an error. | ||
| 0171 | CALL NC,ResetBufferPointer | |||
| 0174 | CALL CompareBuffer | Call CompareBuffer and jump to FeedAllophone_Write if there is something left to say. | ||
| 0177 | JR NZ,FeedAllophone_Write | |||
| 0179 | XOR A | Silence the SP0256 and jump to ExitInterrupt. | ||
| 017A | LD ($1000),A | |||
| 017D | JR ExitInterrupt | |||
|
The SP0256 is idle; write the next queued allophone.
|
||||
| FeedAllophone_Write | 017F | LD A,$06 | Call ReadWorkspace for the next allophone. | |
| 0181 | CALL ReadWorkspace | |||
| 0184 | LD DE,$3000 | Use the low-intonation port (3000). | ||
| 0187 | BIT 6,A | Jump to FeedAllophone_Port if bit 6 is reset. | ||
| 0189 | JR Z,FeedAllophone_Port | |||
| 018B | INC DE | Use the high-intonation port instead (3001). | ||
| FeedAllophone_Port | 018C | LD (DE),A | Select the intonation. | |
| 018D | LD ($1000),A | Write the allophone to the SP0256. | ||
| 0190 | OR A | Work out how many bytes remain in the queue. | ||
| 0191 | SBC HL,BC | |||
| 0193 | INC BC | Step the queue pointer on one byte. | ||
| 0194 | PUSH HL | Stash the remaining length on the stack. | ||
| 0195 | CALL WriteBufferPointer | Call WriteBufferPointer. | ||
| 0198 | LD A,$06 | Call ReadWorkspace for the queue start. | ||
| 019A | CALL ReadWorkspace | |||
| 019D | LD D,H | Point the copy at the previous queue byte. | ||
| 019E | LD E,L | |||
| 019F | DEC HL | |||
| 01A0 | POP BC | Restore the remaining length from the stack. | ||
| 01A1 | LDDR | Shuffle the queued allophones down. | ||
|
Restore the Spectrum registers and page its ROM back in.
|
||||
| ExitInterrupt | 01A3 | POP DE | Restore DE, BC, AF and HL from the stack. | |
| 01A4 | POP BC | |||
| 01A5 | POP AF | |||
| 01A6 | POP HL | |||
| 01A7 | EI | Enable interrupts. | ||
| 01A8 | JP HandleInterrupt | Jump to HandleInterrupt. | ||
| Prev: 0000 | Up: Map | Next: 01AB |