PSG sound
-
- Posts: 41
- Joined: Mon Feb 05, 2018 9:23 am
- Contact:
PSG sound
I would like to add some very simple sound effects in my games but I have no idea how to do it.
CC65 exposes some BIOS functions that should produce some sounds but it is not clear at all to me how they are use.
For example _bios_playsound:
https://github.com/cc65/cc65/blob/maste ... sion/psg.s
Any idea how this BIOS function is supposed to be used?
I need to produce some simple very short beeps and some simple very short noise sounds.
CC65 exposes some BIOS functions that should produce some sounds but it is not clear at all to me how they are use.
For example _bios_playsound:
https://github.com/cc65/cc65/blob/maste ... sion/psg.s
Any idea how this BIOS function is supposed to be used?
I need to produce some simple very short beeps and some simple very short noise sounds.
- @username@
- Posts: 335
- Joined: Tue Oct 22, 2013 6:59 pm
- Location: Scotland
Re: PSG sound
bios_playsound just plays a tune from data you point to.
A lot of the CV ROMs use this function to play a small tune while displaying a demo or welcome screen. Sonic Invaders is a good example.
The downside of bios_playsound is that it is blocking - so you can't do anything while it's playing.
A better alternative is to look at how the TI99 uses songlists, which are called by an ISR.
You could take a songlist from a TI99 or Tutor game - they are the same, and do the same from within your game.
Songlists are simply <COUNT> <PSG_BYTES> which you manage with a pointer.
If you are interested on how to program sound effects on the SN76489 - your best bet is to grab VGMRips and look at the dump from a recording of a game you are playing.
I wrote a ceevee player which plays colecovision sound streams and fx - which is in the forum.
It's a reasonable example of how to play music and fx on an ISR.
A lot of the CV ROMs use this function to play a small tune while displaying a demo or welcome screen. Sonic Invaders is a good example.
The downside of bios_playsound is that it is blocking - so you can't do anything while it's playing.
A better alternative is to look at how the TI99 uses songlists, which are called by an ISR.
You could take a songlist from a TI99 or Tutor game - they are the same, and do the same from within your game.
Songlists are simply <COUNT> <PSG_BYTES> which you manage with a pointer.
If you are interested on how to program sound effects on the SN76489 - your best bet is to grab VGMRips and look at the dump from a recording of a game you are playing.
I wrote a ceevee player which plays colecovision sound streams and fx - which is in the forum.
It's a reasonable example of how to play music and fx on an ISR.
-
- Posts: 41
- Joined: Mon Feb 05, 2018 9:23 am
- Contact:
Re: PSG sound
I need to do something quite simple. I just need short (blocking is fine) beep sounds. If possible I would like to have a couple of noise sounds. I am doing this in C (with little or no Assembly). I am doing this to add minimal sound support to the Creativision target in my Cross-Lib universal 8-bit framework (https://github.com/Fabrizio-Caruso/CROSS-LIB).
I had understood what bios_playsound does but I don't understand how the data is structored.
In CC65 minimal documentation (just a short comment):
https://github.com/cc65/cc65/blob/maste ... sion/psg.s
I cannot figure out the details of:
;* Pass a pointer to a set of note triples, terminated with a tempo byte
;* and the len (max 255)
CC65 exposes
void __fastcall__ bios_playsound(void *a, unsigned char b);
So a seems to be an address where a "tune" is stored somehow but how?
3 bytes (1 byte per channel?) + 1 byte for the tempo, 3 bytes + 1 tempo byte, etc... (len times)
or maybe one tempo byte at the end?
How is a single byte encoding a note?
I just need a bunch of very short (blocking) beep sounds.
I had understood what bios_playsound does but I don't understand how the data is structored.
In CC65 minimal documentation (just a short comment):
https://github.com/cc65/cc65/blob/maste ... sion/psg.s
I cannot figure out the details of:
;* Pass a pointer to a set of note triples, terminated with a tempo byte
;* and the len (max 255)
CC65 exposes
void __fastcall__ bios_playsound(void *a, unsigned char b);
So a seems to be an address where a "tune" is stored somehow but how?
3 bytes (1 byte per channel?) + 1 byte for the tempo, 3 bytes + 1 tempo byte, etc... (len times)
or maybe one tempo byte at the end?
How is a single byte encoding a note?
I just need a bunch of very short (blocking) beep sounds.
- @username@
- Posts: 335
- Joined: Tue Oct 22, 2013 6:59 pm
- Location: Scotland
Re: PSG sound
I would suggest you read this https://www.smspower.org/Development/SN ... opment.PSG
It gives a very good introduction to how the SN76489 produces sounds.
It gives a very good introduction to how the SN76489 produces sounds.
-
- Posts: 41
- Joined: Mon Feb 05, 2018 9:23 am
- Contact:
Re: PSG sound
@username, how do I program the SN76489 on the Creativision? How do I write onto its registers on the Creativision?
- @username@
- Posts: 335
- Joined: Tue Oct 22, 2013 6:59 pm
- Location: Scotland
Re: PSG sound
OK - the creativision has the PSG mapped to $1002. So from C you could just cast a pointer to this address and write bytes to it.
However, the fly in the ointment is propogation time. So use psg_outb. It will forward the command byte you send to $1002, and wait until the PSG acknowledges for you. This is just BIOS $FE77.
To stop sound use psg_silence.
psg_silence just sends $9f,$bf, $df,$ff to the PSG (Set volume off on each channel).
So to replicate the keyboard beep - is just
psg_outb(0x80); // Latch frequency
psg_outb(0x07); // Frequency byte 2
psg_outb(0x90); // Channel 0 full volume
wait a short time
psg_silence();
Good luck!
However, the fly in the ointment is propogation time. So use psg_outb. It will forward the command byte you send to $1002, and wait until the PSG acknowledges for you. This is just BIOS $FE77.
To stop sound use psg_silence.
psg_silence just sends $9f,$bf, $df,$ff to the PSG (Set volume off on each channel).
So to replicate the keyboard beep - is just
psg_outb(0x80); // Latch frequency
psg_outb(0x07); // Frequency byte 2
psg_outb(0x90); // Channel 0 full volume
wait a short time
psg_silence();
Good luck!
-
- Posts: 41
- Joined: Mon Feb 05, 2018 9:23 am
- Contact:
Re: PSG sound
Thanks a lot!
A single beep will improve my games a lot.
After that I will read the PSG specs and try to figure out how to use other commands for things like noise sounds.
A single beep will improve my games a lot.
After that I will read the PSG specs and try to figure out how to use other commands for things like noise sounds.
-
- Posts: 41
- Joined: Mon Feb 05, 2018 9:23 am
- Contact:
Re: PSG sound
@username It works GREAT!
I still have a problem but it may be unrelated to the code. Why do I always here a buzzing sound with Mess/Mame when emulating the Creativision? Is this a Mess/Mame bug? I get the buzzing sound even when the Creativision produces no sound at all.
I still have a problem but it may be unrelated to the code. Why do I always here a buzzing sound with Mess/Mame when emulating the Creativision? Is this a Mess/Mame bug? I get the buzzing sound even when the Creativision produces no sound at all.
- @username@
- Posts: 335
- Joined: Tue Oct 22, 2013 6:59 pm
- Location: Scotland
Re: PSG sound
Creativision Song Format
The song format is in reverse and is terminated with the desired tempo.
So in the above, $0E is the desired tempo. BASIC has a fixed tempo of $12.
The channels are 0, 1, 2 - giving three tone channels.
It's in reverse format, due to the 6502 using Y indexing, the length of the song, in this case $13.
BIOS decrements Y to walk back along the note data, channels are processed 2, 1 then 0.
The channel notes are taken from the BASIC manual, chapter 13 page 102. Duration is from the same manual on page 103.
https://www.madrigaldesign.it/creativemu ... v2_dsw.zip
The encoding is just note number shift left 3, then OR the duration.
So decoding, lower 3 bits gives duration, note number is shift right 3.
For example $4F is Note 9, Duration 7.
There are two volume tables available to the engine.
One at $FCD5 which is a reducing pressure volume table - to try to make the sound more realistic. AirSea Rescue uses this - and it's the default if only the BIOS player is called.
The other is at $FCEB - which is a more digital sound - as the volume is always full. Sonic Invaders uses this one.
Note that BASIC notes 1 and 32 are encoded as 0 - which is a rest.
The pre-computed tables are in BIOS at $FC80 for low frequency.
The high frequency component is at $FCAA.
The song format is in reverse and is terminated with the desired tempo.
Code: Select all
AirSea Title
77 4F 37
4B 05 BB
4F 27 83
93 9B 93
17 4F 96
AB 17 4F
0E
The channels are 0, 1, 2 - giving three tone channels.
It's in reverse format, due to the 6502 using Y indexing, the length of the song, in this case $13.
BIOS decrements Y to walk back along the note data, channels are processed 2, 1 then 0.
The channel notes are taken from the BASIC manual, chapter 13 page 102. Duration is from the same manual on page 103.
https://www.madrigaldesign.it/creativemu ... v2_dsw.zip
The encoding is just note number shift left 3, then OR the duration.
So decoding, lower 3 bits gives duration, note number is shift right 3.
For example $4F is Note 9, Duration 7.
There are two volume tables available to the engine.
One at $FCD5 which is a reducing pressure volume table - to try to make the sound more realistic. AirSea Rescue uses this - and it's the default if only the BIOS player is called.
The other is at $FCEB - which is a more digital sound - as the volume is always full. Sonic Invaders uses this one.
Note that BASIC notes 1 and 32 are encoded as 0 - which is a rest.
The pre-computed tables are in BIOS at $FC80 for low frequency.
The high frequency component is at $FCAA.