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.

You know your heart is already racing...

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.

Color Computer Technical Reference Manual page 31 Audio Multiplexer Hardware

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.

Color Computer Technical Reference Manual page 7 Peripheral Interface Adapter 1 Registers

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

RTS

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies