General guidelines on working with PSG/YM2612 natively
Moderator: BigEvilCorporation
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
General guidelines on working with PSG/YM2612 natively
Hello guys. I finally got my native tracker in a state where I can begin basic sound tests (note editing is finally implemented), and I have to admit I'm really excited about it! In this short two weeks, I've learned how to read and code in 68k assembly (still lots to learn!), work with the VDP, bit manipulation, design a gui, draw a cursor that displays and edits values stored in arrays, FIX BUGS, and so on...
Now the sound chips are new to me. But I intend to keep my nagging for answers to a minimum, I understand that fixing a beginner's problems can probably get old for everyone and I really want to learn as much as I can on my own, so in this topic I'm looking for general tips and guidelines from those who are more expert in this.
What are some common pitfalls and things I need to be aware of? Is working with the sound chips really that much different from working with the video display processor(besides the obvious)? I have the Genesis sound software manual and I already understand on a basic level how this kinda works (VDP was good practice). I just want to get some tips and viewpoints here.
Thanks again guys for your help.
Now the sound chips are new to me. But I intend to keep my nagging for answers to a minimum, I understand that fixing a beginner's problems can probably get old for everyone and I really want to learn as much as I can on my own, so in this topic I'm looking for general tips and guidelines from those who are more expert in this.
What are some common pitfalls and things I need to be aware of? Is working with the sound chips really that much different from working with the video display processor(besides the obvious)? I have the Genesis sound software manual and I already understand on a basic level how this kinda works (VDP was good practice). I just want to get some tips and viewpoints here.
Thanks again guys for your help.
-
- Very interested
- Posts: 60
- Joined: Wed Mar 12, 2014 11:11 pm
- Location: Michigan
- Contact:
I assume that you can write a ton of subroutines that you can jump to when needed. Subroutines such as:
"FM1vol:"
"FM3alg:"
etc.
I can't say for sure, but it really should be as simple (or complex) as talking to any other chip on board. I suggest reading this several times through:
http://www.smspower.org/maxim/Documents/YM2612
and this:
http://www.smspower.org/Development/SN76489
"FM1vol:"
"FM3alg:"
etc.
I can't say for sure, but it really should be as simple (or complex) as talking to any other chip on board. I suggest reading this several times through:
http://www.smspower.org/maxim/Documents/YM2612
and this:
http://www.smspower.org/Development/SN76489
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
Whoa, these are good links. Thanks!
Edit: It looks like the YM link is from the Genesis manual. But maybe there's more that I'm not seeing.
Edit: It looks like the YM link is from the Genesis manual. But maybe there's more that I'm not seeing.
Last edited by Count SymphoniC on Sat Oct 04, 2014 1:58 am, edited 1 time in total.
-
- Very interested
- Posts: 60
- Joined: Wed Mar 12, 2014 11:11 pm
- Location: Michigan
- Contact:
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
-
- Very interested
- Posts: 60
- Joined: Wed Mar 12, 2014 11:11 pm
- Location: Michigan
- Contact:
YUP! Define your global pointers and update registers on the fly.Count SymphoniC wrote:Yes, well defined globals should do the trick.Jazzmarazz wrote:The only set back is that these articles look at the chips from more of a hardware standpoint. you should have no problem manipulating sound if you obsess over the exact locations of each sound register.
Are there set and reset op codes for individual bits?
psudo-code, do not quote me:
Code: Select all
EnableSoundFM1:
LDA FM1reg
SET bit7, A
STA FM1reg
DisableSoundFM1:
LDA FM1reg
RES bit7, A
STA FM1reg
Anyhow, I don't need to tell you that (especially since idk first hand HAHa...).
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
-
- Very interested
- Posts: 60
- Joined: Wed Mar 12, 2014 11:11 pm
- Location: Michigan
- Contact:
Well I know you can pack 1's and 0's with AND and OR logic if you needed so I guess I answered my own question.Count SymphoniC wrote:Basically you're working with bytes, words and long words. There are some bit operations, boolean algebra... but for setting and resetting specific bits like that, other than the boolean opcodes and rotating bits I'm still learning the 68k instruction set.
What I meant to get at was that many of the sound registers are shared with many individual actions. take for example the gameboy:
FF26 -- SNDREG52 [RW] Sound ON/OFF
Bit7 All sound on[1]/off[0]
Bit3 Sound 4 on[1]/off[0]
Bit2 Sound 3 on[1]/off[0]
Bit1 Sound 2 on[1]/off[0]
Bit0 Sound 1 on[1]/off[0]
The YM and PSG will share similar registers, but you will notice that each bit within the byte located at $FF26 does a different thing. Some register do use the whole byte as a value from 0-255, but more often than not it is just used as a single bit to toggle a parameter.
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
Yeah I understand. It's become clear to me that I'll need to investigate bit manipulation with 68k some more, I know I've only tapped the surface of this programming language.
For every problem, there is an infinite number of solutions.
EDIT: I totally forgot, you can add and subtract in binary. This could easily be used to toggle specific bits.
For every problem, there is an infinite number of solutions.
EDIT: I totally forgot, you can add and subtract in binary. This could easily be used to toggle specific bits.
-
- Very interested
- Posts: 122
- Joined: Mon May 07, 2007 5:19 pm
- Location: New York, NY, USA
- Contact:
This reminds me I need to post a thread "What I Learned from Porting YM2612 and SN76489 to JavaScript" soon
For some of the practical aspects of sound playback, the following should be useful:
For some of the practical aspects of sound playback, the following should be useful:
- Remember that Total Level in YM2612 operators and Attenuation in PSG channels is the inverse of "volume;" a value 0 means loudest.
- The YM2612 is marketed as "frequency" modulation synthesis but in practice acts more like "phase" modulation synthesis. You're less manipulating frequencies and more manipulating phase counters. This makes it easier to understand how the operators actually affect each other if you wanted to do a thing like draw the volume envelopes and draw a sample waveform for preview purposes.
- For PSG, even if you do what FamiTracker does and add the ability to define things like "instrument" arpeggios and envelopes and such, you still have to convert those presets to individual events in preview playback, code export, etc. You'd essentially be writing your own sound engine in this case if you don't choose to implement an existing one and export to that format.
- In your code, treat the sound system as a perfect system but expect to need to account for the usual hardware imperfections. To this day, the official volume balance between the PSG and YM on a Genesis is not publicly known, for example.
- For YM, try to be as thorough as possible in your early drafts of implementation (e.g. include things like channel 3 CSM manipulation, channel 6 PCM toggling, and decent SSG register handling as early as possible). It's easier to make too much to start and comment it out later than it is to make too little to start and add things in later.
- If you can, screenshot and disassemble the SPATULA ("YM 2612 Editor by K. Banks (PD)") rom. Its preset editing interface is bulky and crowded, and shows you what NOT to do to have intuitive YM editing. VGM dumping it also resulted in me finding that it sets all the YM registers EVERY NOTE; find a way to avoid redundancies like that in your code export functionality if you can.
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
Thank you for your wisdom and insight. I'll take all of these into account, especially with the YM2612, as it appears to be a pretty complex chip!
I got a tone sounding off on the PSG yesterday which was exciting for a fleeting moment. Then I realized I was going to have to figure out how to read my notation data, and either convert it to PSG reg data or something. The method I came up with was creating a lookup table for each note of the chromatic scale, with it's values ready to be used with the PSG... of course I couldn't find a note table for the PSG, so I'm creating the table by hand, using the formula FDATA=C divided by F*32. FDATA being the frequency data (in hex word) for the chip, C being the PSG clock in hz (integers) and F being the real frequency of the note, I'm a bit irritated with the Frequency part because the real frequency values in hz are decimal numbers, and that won't work too well with hex word values so I'm having to round off these values. I fear I'm going to have to use a tuner once I'm done getting all this working to adjust the results for pitch perfect notes. But I don't see a cheat sheet just lying around so here we go!
I got a tone sounding off on the PSG yesterday which was exciting for a fleeting moment. Then I realized I was going to have to figure out how to read my notation data, and either convert it to PSG reg data or something. The method I came up with was creating a lookup table for each note of the chromatic scale, with it's values ready to be used with the PSG... of course I couldn't find a note table for the PSG, so I'm creating the table by hand, using the formula FDATA=C divided by F*32. FDATA being the frequency data (in hex word) for the chip, C being the PSG clock in hz (integers) and F being the real frequency of the note, I'm a bit irritated with the Frequency part because the real frequency values in hz are decimal numbers, and that won't work too well with hex word values so I'm having to round off these values. I fear I'm going to have to use a tuner once I'm done getting all this working to adjust the results for pitch perfect notes. But I don't see a cheat sheet just lying around so here we go!
-
- Very interested
- Posts: 122
- Joined: Mon May 07, 2007 5:19 pm
- Location: New York, NY, USA
- Contact:
I wrote up a couple of quick functions in JavaScript a while back for converting Hz to FNUM for both the PSG and YM, if you wanted to have that cheat sheet as a web page or something.
Note: the YM_hzToFnum function returns a JS object with 'fnum' and 'block' properties, so keep that in mind if you're converting it to another language for the cheat sheet.
You know what? I've been meaning to put up a proper cheat sheet myself. I'll be back shortly with a link to it.
Code: Select all
function MIDI_noteToHz(n){
return 440*Math.pow(2,(n-69)/12);
};
function MIDI_HzToNote(hz){
var div = hz/440.0;
return 12*Math.log(div)/Math.log(2);
}
Code: Select all
function SN_fnumToHz(clock, fn){ // 3579545 is a good NTSC clock value
if (fn<=0) throw new RangeError("FnumToHz - cannot divide by <= 0");
var div = 0.0625, reg = 1/((fn&0x3ff)<<1);
return div*reg*clock;
};
function SN_hzToFnum(clock, hz) { // 3579545 is a good NTSC clock value
var div = 0.0625, reg = 0.5;
if (hz<=0) throw new RangeError("hzToFnum - cannot divide by <= 0");
return (div*reg*clock/hz)|0; // x|0 is the same as casting as int
};
Code: Select all
function YM_fnumToHz(fn,b) { // defaults to 7670448 NTSC clock value, 6 channels, 24 as the divisor
var rs = 1, clock = 7670448, ch = 6, fm = 24;
if (arguments.length>5) fm = arguments[5];
if (arguments.length>4) ch = arguments[4];
if (arguments.length>3) clock = arguments[3];
if (arguments.length>2) rs = arguments[2];
var cl = ch*fm, pre = clock/(rs*cl);
return fn*pre*Math.pow(2,b-21);
};
function YM_hzToFnum(hz) {
var dp = 4, rs = 1, clock = 7670448, ch = 6, fm = 24;
if (arguments.length>5) fm = arguments[5];
if (arguments.length>4) ch = arguments[4];
if (arguments.length>3) clock = arguments[3];
if (arguments.length>2) rs = arguments[2];
if (arguments.length>1) dp = arguments[1];
var cl = ch*fm, pre = clock/(rs*cl), t = Math.pow(10,dp);
var fn = hz*(1<<14)/pre, b = 8;
while (fn<2048&&(((hz*t)|0)/t)!=(((YM_fnumToHz(fn|0,b)*t)|0)/t)&&--b>0) fn *= 2;
fn>>=1;
return {'fnum':fn|0,'block':b};
};
Wrong assumption that all 12 notes in octave are uniformly distributed.neologix wrote:Code: Select all
function MIDI_noteToHz(n){ return 440*Math.pow(2,(n-69)/12); };
-
- Very interested
- Posts: 149
- Joined: Sat Nov 17, 2012 3:58 am
EDIT: I didn't see the post above this one, so I edited what I said out.r57shell wrote:Wrong assumption that all 12 notes in octave are uniformly distributed.neologix wrote:Code: Select all
function MIDI_noteToHz(n){ return 440*Math.pow(2,(n-69)/12); };
For NTSC 3579545 ÷ (NoteFreqhz * 32) = PSGREGDATA
This is the equation I used to convert from hz to data, to write the specific note to the PSG register... I wrote it poorly in the post above but I haven't been in school for years. I didn't design this chip, I don't know why they do it like this, but that's how it works. The equation gives the correct note output.
EDIT: And of course you have to move things around to satisfy the PSG. See documentation for details.
Last edited by Count SymphoniC on Sun Oct 05, 2014 3:52 am, edited 4 times in total.
-
- Very interested
- Posts: 122
- Joined: Mon May 07, 2007 5:19 pm
- Location: New York, NY, USA
- Contact:
With MIDI almost no one does micro-tuning, so multiplying by 2^(1/12) to get the default frequencies for the 128 MIDI notes for these purposes is safe.r57shell wrote:Wrong assumption that all 12 notes in octave are uniformly distributed.neologix wrote:Code: Select all
function MIDI_noteToHz(n){ return 440*Math.pow(2,(n-69)/12); };
@Count SymphoniC - I've uploaded the cheat sheet to http://www.luxatom.com/md-fnum.html ; it uses NTSC clocks by default, though that's only because I only spent a couple of minutes scripting the tables.