Prev: 00000 Up: Map Next: 00427
00056: Handle Interrupt
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 00056 PUSH AF Preserve AF (this PUSH/POP pair is what Rockfall II checks for).
00057 POP AF
00058 DI Keep further interrupts out while the μSpeech ROM is paged in.
00059 PUSH HL Stash HL, AF, BC and DE on the stack.
00060 PUSH AF
00061 PUSH BC
00062 PUSH DE
The speech workspace is a short block at the top of the reserved area, between *RAMTOP and the UDGs (low byte 87). A 48K machine uses 65367; a 16K machine uses 32599.
00063 LD HL,(23730) Fetch *RAMTOP.
00066 LD A,L Jump to ProbeWorkspace if the low byte is not 87.
00067 CP 87
00069 JR NZ,ProbeWorkspace
00071 LD A,H Jump to InstallWorkspace if *RAMTOP is still the 48K power-on value (65367).
00072 CP 255
00074 JR Z,InstallWorkspace
00076 CP 127 Jump to InstallWorkspace if it is still the 16K power-on value (32599).
00078 JR Z,InstallWorkspace
*RAMTOP is not a power-on value; probe the speech workspace below it.
ProbeWorkspace 00080 LD A,6 Call ReadWorkspace for the address of the queue head (index 6).
00082 CALL ReadWorkspace
00085 LD B,H Copy the workspace address to BC.
00086 LD C,L
00087 CALL CheckBufferSpace Call CheckBufferSpace and jump to InstallWorkspace to reinstall if the workspace is no longer protected by *RAMTOP.
00090 JR C,InstallWorkspace
00092 CALL ReadBufferPointer Call ReadBufferPointer.
00095 CALL CheckBufferSpace Call CheckBufferSpace.
00098 CALL C,EmptyQueue Call EmptyQueue if the buffer pointer is at or below *RAMTOP.
00101 XOR A Call ReadWorkspace for the flags byte.
00102 CALL ReadWorkspace
00105 BIT 2,A Jump to ServiceSpeech if this is the second pass of this interrupt.
00107 JR NZ,ServiceSpeech
00109 SET 2,(HL) Flag that the next entry is the second pass.
00111 POP DE Restore DE, BC, AF and HL from the stack.
00112 POP BC
00113 POP AF
00114 POP HL
Let the Spectrum's IM1 routine (MASK_INT) scan the keyboard first. When it returns, control goes to HandleInterrupt, which pages this ROM back in for the second pass.
ReturnToSpectrum 00115 PUSH HL Leave two copies of HL below the stack.
00116 PUSH HL
00117 POP HL
00118 POP HL
00119 LD HL,56 Stack HandleInterrupt over the first copy, as the return address for the Spectrum IM1 routine.
00122 PUSH HL
00123 DEC SP Restore HL from the stack.
00124 DEC SP
00125 POP HL
00126 JP HandleInterrupt Jump to HandleInterrupt to page the Spectrum ROM in.
Wait a few frames, then reserve a page below *RAMTOP for the speech workspace.
InstallWorkspace 00129 LD A,(23672) Jump to ExitInterrupt if *FRAMES is still less than 5.
00132 CP 5
00134 JP C,ExitInterrupt
00137 XOR A Call ReadWorkspace for the flags byte.
00138 CALL ReadWorkspace
00141 DEC H Set *RAMTOP one page below the workspace page.
00142 LD (23730),HL
00145 INC H Write 130 to the flags byte.
00146 LD (HL),130
00148 DEC HL Clear workspace index 1.
00149 XOR A
00150 LD (HL),A
00151 DEC HL Store the workspace page as the high byte of the buffer pointer (index 2).
00152 LD (HL),H
00153 DEC HL Write 81 as its low byte (index 3): an empty queue.
00154 LD (HL),81
00156 DEC HL Clear workspace indexes 4 and 5.
00157 LD (HL),A
00158 DEC HL
00159 LD (HL),A
00160 LD HL,(23730) Fetch the new *RAMTOP.
Plant a sentinel and move the machine stack below the new *RAMTOP.
InstallWorkspace_Stack 00163 LD (HL),62 Write a 62 sentinel at the new *RAMTOP.
00165 DEC HL
00166 LD (HL),A Write 0 beneath it.
00167 LD SP,HL Point SP one byte below the new *RAMTOP.
00168 DEC HL Clear the two bytes below the new stack.
00169 LD (HL),A
00170 DEC HL
00171 LD (HL),A
Install abandons the interrupted program: MASK_INT returns to MAIN_1, re-entering the BASIC main loop with a fresh stack.
00172 LD (23613),HL Write the error stack to *ERR_SP.
00175 LD DE,4777 Set the return address to MAIN_1 in MAIN_EXEC.
00178 PUSH DE Stash the return address on the stack.
00179 EI Re-enable interrupts before handing over to the Spectrum ROM.
00180 JP HandleInterrupt Jump to HandleInterrupt.
Second pass: clear the pass flag, then draw the banner once and speak.
ServiceSpeech 00183 RES 2,(HL) Clear the second-pass flag.
00185 BIT 7,(HL) Call PrintBanner if the banner has not been drawn yet.
00187 CALL NZ,PrintBanner
The Spectrum IM1 routine runs twice per frame (see ReturnToSpectrum), so take one off *FRAMES to compensate for the extra increment.
00190 LD HL,(23672) Fetch the *FRAMES word.
00193 LD A,H Jump to ServiceSpeech_Tick if the low word of *FRAMES is non-zero.
00194 OR L
00195 JR NZ,ServiceSpeech_Tick
00197 LD HL,23674 Decrease the high byte of *FRAMES if the low word is about to wrap.
00200 DEC (HL)
ServiceSpeech_Tick 00201 LD HL,(23672) Decrease the low word of *FRAMES.
00204 DEC HL
00205 LD (23672),HL
Likewise add one to the repeat and delay counters in each half of *KSTATE, to compensate for KEYBOARD's extra decrement. (When a set is free these counters just drift upwards; KEYBOARD reloads them when a new key arrives.)
00208 LD HL,23554 Point at the keyboard-state timers (4 bytes apart).
00211 LD DE,4
00214 CALL IncrementPair Call IncrementPair.
00217 DEC HL Move back to the five-call counters (23553 and 23557).
00218 CALL IncrementPair Call IncrementPair.
Programs speak by assigning the BASIC string s$. Look it up and expand it into allophones if it exists.
00221 LD A,83 Call FindVariable for "s$".
00223 CALL FindVariable
00226 CALL Z,ParseSpeechString Call ParseSpeechString if s$ exists.
00229 CALL TestKeysVariable Call TestKeysVariable.
00232 JP Z,FeedAllophone Jump to FeedAllophone if key speech is disabled.
00235 XOR A Call ReadWorkspace for the flags byte.
00236 CALL ReadWorkspace
00239 LD A,(23611) Fetch *FLAGS.
00242 BIT 5,A Jump to ServiceSpeech_NoKey if no new key was pressed.
00244 JR Z,ServiceSpeech_NoKey
00246 BIT 4,(HL) Jump to FeedAllophone if this key is already being spoken.
00248 JR NZ,FeedAllophone
00250 JR ServiceSpeech_Key Jump to ServiceSpeech_Key.
ServiceSpeech_NoKey 00252 RES 4,(HL) Clear the "key held" flag and jump to FeedAllophone.
00254 JR FeedAllophone
A new key is in *LAST_K; turn it into allophones.
ServiceSpeech_Key 00256 SET 4,(HL) Set the "key held" flag.
00258 LD A,(23560) Fetch *LAST_K.
00261 CP 123 Jump to ServiceSpeech_NotLetter if the code is 123 or higher.
00263 JR NC,ServiceSpeech_NotLetter
00265 CP 97 Jump to ServiceSpeech_NotLetter if the code is below "a".
00267 JR C,ServiceSpeech_NotLetter
00269 SUB 32 Convert a lowercase letter to uppercase and jump to ServiceSpeech_Lookup.
00271 JR ServiceSpeech_Lookup
Not a letter. Graphics characters (block graphics and UDGs) share key 128; other symbols go through PunctuationKeys.
ServiceSpeech_NotLetter 00273 CP 165 Jump to ServiceSpeech_Punctuation if the code is 165 or higher.
00275 JR NC,ServiceSpeech_Punctuation
00277 CP 128 Jump to ServiceSpeech_Punctuation if the code is below 128.
00279 JR C,ServiceSpeech_Punctuation
00281 LD A,128 Use dictionary key 128 and jump to ServiceSpeech_Lookup.
00283 JR ServiceSpeech_Lookup
ServiceSpeech_Punctuation 00285 LD HL,1069 Search PunctuationKeys for this character.
00288 LD BC,13
00291 CPIR Jump to ServiceSpeech_Lookup if there is no mapping.
00293 JR NZ,ServiceSpeech_Lookup
00295 LD BC,12 Fetch the replacement from PunctuationValues.
00298 ADD HL,BC
00299 LD A,(HL)
Look the key up in AllophoneDictionary.
ServiceSpeech_Lookup 00300 LD HL,1095 Point at AllophoneDictionary.
00303 LD B,A Keep the key in B.
ServiceSpeech_FindKey 00304 BIT 7,(HL) Jump to ServiceSpeech_NextKey if this byte ends an allophone list.
00306 JR NZ,ServiceSpeech_NextKey
00308 INC HL Move to the next dictionary byte.
00309 JR ServiceSpeech_FindKey Jump to ServiceSpeech_FindKey.
ServiceSpeech_NextKey 00311 INC HL Move to the next dictionary key.
00312 XOR A Jump to FeedAllophone if the table has ended.
00313 CP (HL)
00314 JR Z,FeedAllophone
00316 LD A,B Jump to ServiceSpeech_Copy if this key matches.
00317 CP (HL)
00318 JR Z,ServiceSpeech_Copy
00320 INC HL Move to the next dictionary byte.
00321 JR ServiceSpeech_FindKey Jump to ServiceSpeech_FindKey.
Copy the matching allophones into the speech buffer.
ServiceSpeech_Copy 00323 INC HL Move to the first allophone of this entry.
00324 CALL TestQueueEmpty Call TestQueueEmpty and jump to ServiceSpeech_Store if the queue is empty.
00327 JR Z,ServiceSpeech_Store
00329 INC BC Step the buffer pointer on one byte.
00330 LD A,(BC) Jump to FeedAllophone (leaving the key unspoken) if the last queued allophone does not have bit 7 set.
00331 RLA
00332 JR NC,FeedAllophone
00334 CALL EmptyQueue Call EmptyQueue to empty the queue so the new key word replaces the previous one.
ServiceSpeech_Store 00337 LD A,(HL) Queue this allophone in the speech buffer.
00338 LD (BC),A
00339 DEC BC
00340 CALL CheckBufferSpace Call CheckBufferSpace and jump to ServiceSpeech_Flush if the buffer has hit *RAMTOP.
00343 JR C,ServiceSpeech_Flush
00345 BIT 7,(HL) Jump to ServiceSpeech_Commit if this was the last allophone of the word.
00347 JR NZ,ServiceSpeech_Commit
00349 INC HL Move to the next allophone.
00350 JR ServiceSpeech_Store Jump to ServiceSpeech_Store.
ServiceSpeech_Flush 00352 CALL ReadBufferPointer Call ReadBufferPointer.
ServiceSpeech_Commit 00355 CALL WriteBufferPointer Call WriteBufferPointer.
The SP0256 is the speech-synthesiser chip. Read its status at 4096 (bit 0 is the inverted load request) and send the next queued allophone if the input buffer can take one.
FeedAllophone 00358 LD A,(4096) Read the SP0256 status from 4096.
00361 BIT 0,A Jump to ExitInterrupt if the chip's input buffer is full.
00363 JR NZ,ExitInterrupt
The manual notes that "the unit will remain silent until the first ENTER is pressed after a power up, a NEW, or after a 'report' has been printed at the bottom of the screen." On every interrupt that reaches this code where bit 7 of *ERR_NR is reset (an error code is waiting), this code empties the speech queue through EmptyQueue, so nothing is spoken until the ROM sets *ERR_NR back to 255; that happens when ENTER is pressed and on the editor's error paths (ED_ERROR at 4229 and ED_FULL at 4466 in the Spectrum ROM). The power-on and NEW cases are from the manual (chapter 2 also says any report other than OK has this effect).
00365 LD A,(23610) Empty the speech queue (EmptyQueue) if BASIC is reporting an error (bit 7 of *ERR_NR reset).
00368 RLA
00369 CALL NC,EmptyQueue
00372 CALL TestQueueEmpty Call TestQueueEmpty and jump to FeedAllophone_Write if anything is queued.
00375 JR NZ,FeedAllophone_Write
00377 XOR A Keep the chip fed with PA1 (silence) and jump to ExitInterrupt.
00378 LD (4096),A
00381 JR ExitInterrupt
The SP0256 can take an allophone; write the next queued one.
FeedAllophone_Write 00383 LD A,6 Call ReadWorkspace for the next allophone.
00385 CALL ReadWorkspace
00388 LD DE,12288 Point at 12288 (low pitch) or 12289 (high pitch) by bit 6.
00391 BIT 6,A
00393 JR Z,FeedAllophone_Port
00395 INC DE
FeedAllophone_Port 00396 LD (DE),A Select the pitch (only the address matters; it switches the speech chip's clock between about 3.05 MHz and 3.26 MHz (MAME's values; see Intonation)).
00397 LD (4096),A Write the allophone to the speech port (the chip ignores bits 6 and 7).
00400 OR A Work out the queue length (including the allophone just sent).
00401 SBC HL,BC
00403 INC BC Step the queue pointer on one byte.
00404 PUSH HL Stash the queue length on the stack.
00405 CALL WriteBufferPointer Call WriteBufferPointer.
00408 LD A,6 Call ReadWorkspace for the queue start.
00410 CALL ReadWorkspace
00413 LD D,H Point the copy at the previous queue byte.
00414 LD E,L
00415 DEC HL
00416 POP BC Restore the queue length from the stack.
00417 LDDR Move the rest of the queue up one byte.
Restore the Spectrum registers and page its ROM back in.
ExitInterrupt 00419 POP DE Restore DE, BC, AF and HL from the stack.
00420 POP BC
00421 POP AF
00422 POP HL
00423 EI Re-enable interrupts before handing over to the Spectrum ROM.
00424 JP HandleInterrupt Jump to HandleInterrupt.
Prev: 00000 Up: Map Next: 00427