The Speech/Sound Cartridge talks, as long as the CoCo 3 slows down

/ Tandy CoCo 3 / updated 2026-09-26 coco3 speech-sound-cartridge multi-pak nitros-9 6809 basic09 asm

With the clock mod out, the 6309 CoCo 3 stayed up long enough for me to finally give the Speech/Sound Cartridge a real try. It talks now. I wrote a small say command in 6809 assembly under NitrOS-9 that speaks whatever you type after it. The catch is that the CPU has to drop to slow speed while it speaks.

The SSC and the CoCo SDC in the Multi-Pak, with say on the CM-8.
The SSC and the CoCo SDC in the Multi-Pak, with say on the CM-8.

The Multi-Pak comes out

The SSC and the CoCo SDC both want the one cartridge slot, so the Multi-Pak finally earned its desk space. The SSC is in slot 1, the SDC is in slot 4, and the switch is on 4 so it still boots NitrOS-9 off the SDC.

AddressUse
$FF7EData and status. Bit 7 set means ready for a character. Write text and end with a CR ($0D) to speak.
$FF7DReset, write 1 then 0
$FF01 / $FF03PIA0 CA2 low and CB2 high select the cartridge on the sound mux
$FF23PIA1 CB2 high turns sound output on
$FFD8 / $FFD9CoCo 3 CPU slow (0.89 MHz) or fast (1.79 MHz), any write

At first the SSC looked stuck and say timed out. Resetting and reseating the cartridge is what got it going, and after that it assembled and ran. Old tech. I need to check the simple stuff like that first.

Speech is a faint blip at 1.79 MHz

NitrOS-9 runs the CoCo 3 in double speed. In fast mode the speech comes out as a faint blip. At slow speed it’s clear. From what I’ve read, the SSC’s op-amp gets its negative rail from the E clock, and that rail sags when the clock doubles. I haven’t measured it.

say drops the CPU to slow speed and leaves it there, because the SSC keeps talking after the program exits. When the speech is done, a BASIC09 procedure called fastmode puts it back.

say.asm

This assembles with the native OS-9 asm, which knows the 6309. The rma on the disk uses a different module syntax and only does 6809. I don’t use os9defs either. It fills up asm’s symbol table, and the os9defs.a on disk is rma style and leaves Prgrm, Objct and ReEnt undefined, so each program defines the few symbols it needs with equ.

         nam   say

* --- definitions (instead of os9defs) ---
Prgrm    equ   $10         module type: program
Objct    equ   $01         language: 6809 object code
ReEnt    equ   $80         attribute: reentrant
F$Exit   equ   $06         system call: exit process

rev      set   1
tylg     set   Prgrm+Objct
atrv     set   ReEnt+rev

         mod   eom,name,tylg,atrv,start,size

* --- data area ---
         org   0
stack    rmb   200
size     equ   .

name     fcs   /say/
         fcb   1           edition

* --- main: X -> command line, CR terminated ---
start    sta   >$FFD8      slow CPU to 0.89 MHz (any write works)
         bsr   sndon
loop     lda   ,x+         next character
         bsr   putc
         cmpa  #$0D        sent the CR yet?
         bne   loop
         clrb              no error
         os9   F$Exit

* --- send A to SSC, wait for ready (bit 7 set) ---
* --- gives up after 65536 tries, exits error 1 ---
putc     pshs  a,x
         ldx   #0
wait     lda   >$FF7E
         bmi   ready       bit 7 set = ready
         leax  -1,x
         bne   wait
         puls  a,x
         ldb   #1          timeout
         os9   F$Exit
ready    puls  a,x
         sta   >$FF7E
         rts

* --- route cartridge audio to the TV ---
sndon    lda   >$FF01
         anda  #$C7        clear CA2 control bits
         ora   #$30        CA2 = output low
         sta   >$FF01
         lda   >$FF03
         ora   #$38        CB2 = output high
         sta   >$FF03
         lda   >$FF23
         ora   #$38        CB2 high = sound on
         sta   >$FF23
         rts

         emod
eom      equ   *
         end

OS-9 hands the program X pointing at the command line, already terminated with a CR, which is exactly what the SSC wants. putc saves A and X, so the cmpa #$0D checks the character it just sent. sndon only touches bits 3 to 5 of each PIA control register, the same as the POKEs from BASIC. If the cartridge never goes ready, say exits with ERROR #001.

Getting better at BASIC09

BASIC09 has no immediate mode, so even a one-liner goes in a procedure. I wrote two small ones and packed them so they run from the shell.

cls clears the screen, because for some reason NitrOS-9 doesn’t come with a cls or clear. It just sends a form feed:

SHELL "display 0c"

fastmode puts the CPU back in fast mode after say leaves it slow:

POKE $FFD9,0

Things that tripped me up

Still open