ONE BIT SOUND
The Tandy Color Computer 1, 2 and 3 were a bit unique in their sound capabilities - especially for 1980.
Many other electronic devices of the time were built to output sound using additional synthesis chips such as the General Instruments AY-3-8910 Programmable Sound Generator (PSG) also used in arcade games or the legendary MOS6581 Sound Interface Device (SID) series chips found in many Commodore computers.
But some computers only contain a 1-bit “beeper” - with or without access to a programmable interval timer.
The Color Computers were built around *four* sound systems, most of which required direct MPU control to generate sounds:
- A 1-bit audio port “beeper”
- A 6-bit Digital/Audio Converter (DAC) used for 64-level sound output or 64-level joystick input sampling.
- Sound input from an external Cassette player
- Sound input from an external Cartridge attached to the expansion port (such as the Orchestra 90/CC or the Speech/Sound Pak)
While this requires a lot of MPU time to control, it is an extremely flexible and expandable system.
One interesting feature of the CoCo design is that the 1-bit audio is not controlled by the internal sound multiplexer.
This allows you to generate a variety of square wave beeps at the same time the 6-bit DAC is being used for joystick input or other audio output.
The principal concept of 1-bit audio is that you rapidly toggle a single bit I/O line on and off to generate a Pulse wave sound.
ONE BIT SOUND?
For the Color Computer, possibly one of the most famous uses of the 1-bit sound was the infamous heartbeat in Dungeons of Daggorath, which could play simultaneously with the other monster sounds played through the DAC/multiplexer.
PROGRAMMER'S INFO
The two main places to learn about the 1-bit audio (besides direct from the Madman…) are:
HARDWARE
In this diagram from the Color Computer Technical Reference Manual page 31, you can see the table of which sound sources (6-bit DAC, Cassette, Cartridge) the audio multiplexer can route to the TV speaker.
If you look carefully, you can also see “PIA U4-11” bypasses the audio multiplexer and connects directly to sound output through a 10k resister R22.
PIA REGISTERS
The Color Computer Technical Reference Manual page 7 gives a nice overview and breakdown of the Peripheral Interface Adapter (PIA) registers needed to generate 1-bit (or 6-bit) sounds.
There is a bit of a trick here - there are 4 memory mapped addresses giving access to the 6 registers in the PIA.
Bit 2 of each port (A or B) control register allows you to switch the port from either the Data Port (where you read and write values to the I/O devices) to the Data Direction Register (where you configure each bit of the port as either an input or an output pin).
GENERAL PROGRAMMING THEORY
The general theory of how to generate 1-bit audio goes in two parts
- SETUP:
- (Optionally) use the PIA1 Control Register B ($FF23) bit 3 to turn off DAC, Cassette, or Cartridge sound output through the multiplexer
- Configure the PIA1 Control Register B ($FF23:[2]) bit 2 to switch $FF22 from Data Port B to Data Direction Register B
- Configure Bit 1 of PIA 1 Data Direction Register B ($FF22:[1]) bit 1 ON to set this pin for OUTPUT mode.
- Configure the PIA1 Control Register B ($FF23:[2]) bit 2 to switch $FF22 back to Data Port B from Data Direction Register B
- PLAY A SINGLE TONE
- Output a 1 / ON bit to PIA 1 Data Port B ($FF22:[1]) bit 1
- Countdown or loop for some number of cycles
- Output a 0 / OFF bit to PIA1 Data Port B ($FF22:[1]) bit 1
- Countdown or loop some number of cycles
- Update your counters before looping back to play the next TONE.
EXAMPLE
The example code below generates a square wave with a 50% duty cycle - 2000 cycles with the wave/bit ON and another 2000 cycles with the wave/bit off - before adjusting the cycle count.
Note - the cycles are MPU loop counts, not exact frequencies in Hertz.
****************************************************************************** *** ONEBIT.S *** ONE-BIT SOUND EXAMPLE FOR TANDY COLOR COMPUTER 1/2/3 *** BY R. ALLEN MURPHEY *** EXAMPLE BUILD INSTRUCTIONS *** >>> vi onebit.s *** >>> lwasm -9 -p cd --list=onebit.txt -o onebit.bin onebit.s *** >>> perl -e 'print chr(255) x (35*18*256)' > ONEBIT.DSK *** >>> decb dskini ONEBIT.DSK *** >>> decb kill ONEBIT.DSK,ONEBIT.BIN *** >>> decb copy onebit.bin ONEBIT.DSK,ONEBIT.BIN -2 *** >>> mame coco3h -ramsize 2M -window -resolution 1920x1080 -skip_gameinfo -flop1 ONEBIT.DSK & ****************************************************************************** * DEFINE SOME I/O PORT EQUATES PIA1AD EQU $FF20 * PIA 1 PORT A DATA PIA1AC EQU $FF21 * PIA 1 PORT A CONTROL PIA1BD EQU $FF22 * PIA 1 PORT B DATA PIA1BC EQU $FF23 * PIA 1 PORT B CONTROL * START SOMEWHERE ARBITRARY IN RAM - IMPLIES 32K+ COCO1/2/3 -MOVE LOWER FOR 4/16K ORG $4000 * FIRST WE'RE GOING TO SETUP THE COCO PIAS FOR ONE-BIT SOUND ONEBIT * STEP 1: DISABLE SNDEN LINE SILENCING DAC * NOTE: YOU DO THIS ANYWAY FOR READING JOYSTICKS TOO LDA PIA1BC * PIA1 PORT B CONTROL ANDA #%11110111 * CLEAR BIT 3=0 SIXBIT SOUND DISABLE STA PIA1BC * UPDATE PORT B CONTROL * STEP 2: CONFIGURE PIA1 PORT B DATA REGISTER TO DATA DIR REGISTER LDA PIA1BC * PIA1 PORT B CONTROL (AGAIN) ANDA #%11111011 * CLEAR BIT 2=0 FF22 AS DATA DIR REGISTER STA PIA1BC * UPDATE PORT B CONTROL * STEP 3: NOW CONFIGURE PIA1 PORT B SO BIT 2 IS OUTPUT NOT INPUT * I SUSPECT TANDY NEVER BOTHERED TO UPDATE PORT AFTER RESETS LDA PIA1BD * PIA1 PORT B DATA DIR REGISTER ORA #%00000010 * SET BIT 2=1 ONEBIT SOUND OUTPUT ENABLE STA PIA1BD * UPDATE PORT B DATA DIR REGISTER * STEP 4: SET PIA1 PORT B REGISTER BACK TO DATA PORT FROM DATA DIR LDA PIA1BC * PIA1 PORT B CONTROL (AGAIN) ORA #%00000100 * SET BIT 2=1 CONFIGURE FF22 AS DATA PORT B STA PIA1BC * UPDATE PORT B CONTROL * OKAY THE BASIC SETUP IS DONE NOW LET'S MAKE SOME NOISE WITH IT LDX #2000 * START WITH 2000 AS A TONE PERIOD STX TONE * STORE IT IN A 16 BIT SPOT * A ONEBIT SOUND IS A REPEATING ON OFF PATTERN * AND THE TONE IS CONTROLLED BY THE PERIOD OF TIME BETWEEN ON-TO-OFF-TO-ON AGAIN * SO LETS TURN ON THE ONEBIT FOR "A WHILE" DEFINED BY TONE PERIOD BITON * TURN ON THE ONE-BIT SOUND OUTPUT LDA PIA1BD * GET THE ONEBIT OUTPUT PORT ADDRESS ORA #%00000010 * SET BIT 2=1 STARTING SOUND OUT STA PIA1BD * UPDATE PIA1 PORT B AND START MAKING NOISE * ONCE THE NOISE STARTS WE SPEND "SOME TIME" LETTING IT MAKE NOISE * THE TIME IS DEFINED AS COUNTER OF SOME SORT * HERE A SIMPLE COUNTDOWN LOOP HOGS UP THE CPU * REAL IMPLEMENTATIONS SHOULD REFER TO SIMON "INVISIBLE MAN" JONASSEN'S 1BITPLAYER * TO SEE HOW TO DO THIS SUPER EFFICIENTLY UNDER IRQ CONTROL FOR BLAZING SPEED * http://www.roust-it.dk/coco/1bitplayer.asm LDX TONE * GET THE CURRENT VALUE FOR TONE LOOPON * START A COUNTDOWN LOOP LEAX -1,X * COUNTDOWN BY 1 BNE LOOPON * IF COUNTER NOT ZERO LOOP BACK * SO NOT WE'VE PLAYED THE NOISE FOR A WHILE LET'S CREATE AN EQUAL SILENCE BITOFF * TURN OFF THE ONE-BIT SOUND OUTPUT LDA PIA1BD * GET THE ONEBIT OUTPUT PORT ADDRESS ANDA #%11111101 * CLEAR BIT 2=0 STOPPING SOUND OUT STA PIA1BD * UPDATE PIA1 PORT B TO STOP NOISE * ONCE THE NOISE STOPS WE SPEND "SOME TIME" ENJOYING THE SILENCE * AGAIN WE DEFINE THE SILENT PERIOD USING A COUNTDOWN LDX TONE * GET THE CURRENT VALUE FOR TONE LOOPOFF * START A COUNTDOWN LOOP LEAX -1,X * COUNTDOWN BY 1 BNE LOOPOFF * IF COUNTER NOT ZERO LOOP BACK * OKAY WE'VE HAD A PERIOD WITH SOUND ON THEN OFF IN EQUAL MEASURE * FROM HERE ON OUT THE SKY AND OUR IMAGINATION IS THE LIMIT * FOR THIS SIMPLE DEMONSTRATION, WE CAN UPDATE THE TONE VALUE AND LOOP * THIS WILL CREATE A RISING OR FALLING TONE DEPENDING ON HOW WE UPDATE * THE VALUE - MAKING THE TONE PERIOD SMALLER MAKES FOR A RISING SOUND LDX TONE * GET THE CURRENT VALUE FOR TONE LEAX -1,X * SUBTRACT ONE STX TONE * UPDATE THE CURRENT TONE VALUE BRA BITON * GO BACK AND START THE ON-OFF CYCLES AGAIN * WE'LL USE 2 BYTES TO HOLD A 16-BIT TONE PERIOD TONE RMB 2 * AND WE'RE DONE - EXECUTE STARTING AT ADDRESS OF ONEBIT END ONEBIT ****************************************************************************** *** END OF ONEBIT.S ******************************************************************************
GOING FURTHER
In addition to changing the TONE value, a next step to vary the sounds generated is to alter the ratio of cycles where the bit is on compared to the time the bit is off.
For example, if you set the bit on to 100 and the bit off to 900 - you still get 1000 cycles, but a different sound from the “pin pulse” than the square wave.
Pin pulses also allow “mixing” multiple single-bit channels into one output, allowing 2-4 channels of tones to be a played together, using Pulse Width Modulation (PWM).
EXAMPLE TWO
In this example, which is simply a slight variation of the example above, the duty cycle or pulse widths are varied for the same tone rather than changing the tone itself.
The main change is at the top of the BITOFF code where I subtract the current tone value from 1000 and use that as the BIT OFF time.
This way as tone counts down from 999 to 0 for BITON, the BITOFF time simultaneously counts up from 1 to 999.
****************************************************************************** *** ONEBIT.S *** ONE-BIT SOUND EXAMPLE FOR TANDY COLOR COMPUTER 1/2/3 *** BY R. ALLEN MURPHEY *** EXAMPLE BUILD INSTRUCTIONS *** >>> vi onebit.s *** >>> lwasm -9 -p cd --list=onebit.txt -o onebit.bin onebit.s *** >>> perl -e 'print chr(255) x (35*18*256)' > ONEBIT.DSK *** >>> decb dskini ONEBIT.DSK *** >>> decb kill ONEBIT.DSK,ONEBIT.BIN *** >>> decb copy onebit.bin ONEBIT.DSK,ONEBIT.BIN -2 *** >>> mame coco3h -ramsize 2M -window -resolution 1920x1080 -skip_gameinfo -flop1 ONEBIT.DSK & ****************************************************************************** * DEFINE SOME I/O PORT EQUATES PIA1AD EQU $FF20 * PIA 1 PORT A DATA PIA1AC EQU $FF21 * PIA 1 PORT A CONTROL PIA1BD EQU $FF22 * PIA 1 PORT B DATA PIA1BC EQU $FF23 * PIA 1 PORT B CONTROL * START SOMEWHERE ARBITRARY IN RAM - IMPLIES 32K+ COCO1/2/3 -MOVE LOWER FOR 4/16K ORG $4000 * FIRST WE'RE GOING TO SETUP THE COCO PIAS FOR ONE-BIT SOUND ONEBIT * STEP 1: DISABLE SNDEN LINE SILENCING DAC * NOTE: YOU DO THIS ANYWAY FOR READING JOYSTICKS TOO LDA PIA1BC * PIA1 PORT B CONTROL ANDA #%11110111 * CLEAR BIT 3=0 SIXBIT SOUND DISABLE STA PIA1BC * UPDATE PORT B CONTROL * STEP 2: CONFIGURE PIA1 PORT B DATA REGISTER TO DATA DIR REGISTER LDA PIA1BC * PIA1 PORT B CONTROL (AGAIN) ANDA #%11111011 * CLEAR BIT 2=0 FF22 AS DATA DIR REGISTER STA PIA1BC * UPDATE PORT B CONTROL * STEP 3: NOW CONFIGURE PIA1 PORT B SO BIT 2 IS OUTPUT NOT INPUT * I SUSPECT TANDY NEVER BOTHERED TO UPDATE PORT AFTER RESETS LDA PIA1BD * PIA1 PORT B DATA DIR REGISTER ORA #%00000010 * SET BIT 2=1 ONEBIT SOUND OUTPUT ENABLE STA PIA1BD * UPDATE PORT B DATA DIR REGISTER * STEP 4: SET PIA1 PORT B REGISTER BACK TO DATA PORT FROM DATA DIR LDA PIA1BC * PIA1 PORT B CONTROL (AGAIN) ORA #%00000100 * SET BIT 2=1 CONFIGURE FF22 AS DATA PORT B STA PIA1BC * UPDATE PORT B CONTROL * OKAY THE BASIC SETUP IS DONE NOW LET'S MAKE SOME NOISE WITH IT LDX #999 * START WITH 2000 AS A TONE PERIOD STX TONE * STORE IT IN A 16 BIT SPOT * A ONEBIT SOUND IS A REPEATING ON OFF PATTERN * AND THE TONE IS CONTROLLED BY THE PERIOD OF TIME BETWEEN ON-TO-OFF-TO-ON AGAIN * SO LETS TURN ON THE ONEBIT FOR "A WHILE" DEFINED BY TONE PERIOD BITON * TURN ON THE ONE-BIT SOUND OUTPUT LDA PIA1BD * GET THE ONEBIT OUTPUT PORT ADDRESS ORA #%00000010 * SET BIT 2=1 STARTING SOUND OUT STA PIA1BD * UPDATE PIA1 PORT B AND START MAKING NOISE * ONCE THE NOISE STARTS WE SPEND "SOME TIME" LETTING IT MAKE NOISE * THE TIME IS DEFINED AS COUNTER OF SOME SORT * HERE A SIMPLE COUNTDOWN LOOP HOGS UP THE CPU * REAL IMPLEMENTATIONS SHOULD REFER TO SIMON "INVISIBLE MAN" JONASSEN'S 1BITPLAYER * TO SEE HOW TO DO THIS SUPER EFFICIENTLY UNDER IRQ CONTROL FOR BLAZING SPEED * http://www.roust-it.dk/coco/1bitplayer.asm LDX TONE * GET THE CURRENT VALUE FOR TONE LOOPON * START A COUNTDOWN LOOP LEAX -1,X * COUNTDOWN BY 1 BNE LOOPON * IF COUNTER NOT ZERO LOOP BACK * SO NOT WE'VE PLAYED THE NOISE FOR A WHILE LET'S CREATE AN EQUAL SILENCE BITOFF * TURN OFF THE ONE-BIT SOUND OUTPUT LDA PIA1BD * GET THE ONEBIT OUTPUT PORT ADDRESS ANDA #%11111101 * CLEAR BIT 2=0 STOPPING SOUND OUT STA PIA1BD * UPDATE PIA1 PORT B TO STOP NOISE * ONCE THE NOISE STOPS WE SPEND "SOME TIME" ENJOYING THE SILENCE * AGAIN WE DEFINE THE SILENT PERIOD USING A COUNTDOWN LDD #1000 SUBD TONE TFR D,X * GET THE CURRENT VALUE FOR TONE LOOPOFF * START A COUNTDOWN LOOP LEAX -1,X * COUNTDOWN BY 1 BNE LOOPOFF * IF COUNTER NOT ZERO LOOP BACK * OKAY WE'VE HAD A PERIOD WITH SOUND ON THEN OFF IN EQUAL MEASURE * FROM HERE ON OUT THE SKY AND OUR IMAGINATION IS THE LIMIT * FOR THIS SIMPLE DEMONSTRATION, WE CAN UPDATE THE TONE VALUE AND LOOP * THIS WILL CREATE A RISING OR FALLING TONE DEPENDING ON HOW WE UPDATE * THE VALUE - MAKING THE TONE PERIOD SMALLER MAKES FOR A RISING SOUND LDX TONE * GET THE CURRENT VALUE FOR TONE LEAX -1,X * SUBTRACT ONE STX TONE * UPDATE THE CURRENT TONE VALUE BRA BITON * GO BACK AND START THE ON-OFF CYCLES AGAIN * WE'LL USE 2 BYTES TO HOLD A 16-BIT TONE PERIOD TONE RMB 2 * AND WE'RE DONE - EXECUTE STARTING AT ADDRESS OF ONEBIT END ONEBIT ****************************************************************************** *** END OF ONEBIT.S ******************************************************************************
REFERENCES
[1] William “lostwizard” Astle's LWTools http://www.lwtools.ca/
[2] Boisy Pitre, Tim Lindner, and Tormod Volden's ToolShed https://sourceforge.net/projects/toolshed/
SEE ALSO
What is One-Bit Music?
https://www.ludomusicology.org/2018/12/09/what-is-1-bit-music/
The 1-Bit Instrument - The Fundamentals of 1-Bit
https://online.ucpress.edu/jsmg/article/1/1/44/2337/The-1-Bit-InstrumentThe-Fundamentals-of-1-Bit
1-Bit Forum - Other Platforms
http://randomflux.info/1bit/viewforum.php?id=5
One-Bit Audio: An Overview
http://www.sonicstudio.com/pdf/papers/1bitOverview.pdf
Simon “Madman” Jonassen's 1bitplayer.asm
http://www.roust-it.dk/coco/1bitplayer.asm
Simon “Madman” Jonassen's 1bithzix
https://www.facebook.com/groups/2359462640/permalink/10154963599347641/
Simon “Madman” Jonassen's 1bitfirq.asm
https://www.facebook.com/groups/2359462640/permalink/10154963751682641/
RTS
Return to Tandy Color Computer