Use the PSG

From F256 Foenix
Jump to navigationJump to search

The PSG is inside the FPGA of the F256Jr, F256K, F256Jr2 and F256K2

The F256 machines have a dual PSG based on the SN76489 inside the Beatrix FPGA which generate 3 tones of square wave and one noise generator each.

Register Name Address Description
PSG_LEFT 0xD600 Send all formatted commands here to the left PSG
PSG_RIGHT 0xD610 Send all formatted commands here to the right PSG
PSG_COMBINED_LEFT_RIGHT 0xD608 Send all formatted commands here to both PSG
SYS1 0xD6A1 System Ctrl reg: bit2 sets PSG_ST = psg status. if 0, left and right are mixed to monaural output. if 1, left and right are normal stereo

Command Formats

D7 D6 D5 D4 D3 D2 D1 D0 Command Description
1 R2 R1 R0 F3 F2 F1 F0 R's: which register to affect (see below), F's: low four bits of freq
0 X F9 F8 F7 F6 F5 F4 X: unused, F's: high six bits of freq
1 R2 R1 R0 X FB F1 F0 X: unused, FB: control type, F's: frequency of noise gn.
1 R2 R1 R0 A3 A2 A1 A0 R's register target, A's: four attenuation bits
R2 R1 R0 Channel Purpose
0 0 0 Tone 1 Frequency
0 0 1 Tone 1 Attenuation
0 1 0 Tone 2 Frequency
0 1 1 Tone 2 Attenuation
1 0 0 Tone 3 Frequency
1 0 1 Tone 3 Attenuation
1 1 0 Noise Control
1 1 1 Noise Attenuation

How to use the master formula with frequencies

To get to the PSG clock, you start with a 315 000 000 Hz reference divided by 22, yielding 14,318,181.81... (81 repeating) called the 14.31818 MHz NTSC M color subcarrier. Then, you divided it again by 4, yielding 3,579,545 Hz as the PSG master clock..

To generate the values needed for the PSG register, follow this procedure:

1) divide the PSG master clock of 3,579,545 Hz by 32, and then also divide that by the frequency in Hz of the note. n = psg_master_clock / (32 * f) where n is the number destined for the registers and f is the frequency in Hz.

2) convert the integer rounded number you get into a 10-bit resolution binary number

3) the bottom 4 bits are sent to the first part of a "frequency command" while the top 6 bits are used in the second part of the same "frequency command". See examples below and see pre-generated values even further below.

Examples:

These examples focus on PSG1, so send these bytes to address 0xD600 (left PSG)

0b1001 0100 Attenuation command that targets PSG1, Tone 1 to set a middle volume. This can be used as a "note on" event if you had set the frequency prior to this.
0b1000 1101 Frequency setting command part 1, that targets PSG1, Tone 1. This sends the first lower 4 bits of information based on a transformed 440 Hz
0b0000 1111 Frequency setting command part 2, that targets PSG1, Tone 1. This sends the final higher 6 bits of information based on a transformed 440 Hz
0b1001 1111 Attenuation command that targets PSG1, Tone 1 to completely turn off its volume. This can be used as a "note off" event

These examples focus on PSG2, Tone 2, so send these bytes to address 0xD610 (right PSG)

0b1011 0100 Attenuation command that targets PSG2, Tone 2 to set a middle volume. This can be used as a "note on" event if you had set the frequency prior to this.
0b1010 1001 Frequency setting command part 1, that targets PSG2, Tone 2. This sends the first lower 4 bits of information based on a transformed 208 Hz G#
0b0010 0001 Frequency setting command part 2, that targets PSG2, Tone 2. This sends the final higher 6 bits of information based on a transformed 208 Hz G#
0b1011 1111 Attenuation command that targets PSG2, Tone 2 to completely turn off its volume. This can be used as a "note off" event

Frequency bytes in arrays meant for the C language, hard coded

Command with high bits for the frequency are always the same

uint8_t psgHigh[] = {

0x3f, 0x3b,0x38,0x35,0x32,0x2f,0x2c,0x2a,0x27,0x25,0x23,0x21,

0x1f, 0x1d,0x1c,0x1a,0x19,0x17,0x16,0x15,0x13,0x12,0x11,0x10,

0x0f, 0x0e,0x0e,0x0d,0x0c,0x0b,0x0b,0x0a,0x09,0x09,0x08,0x08,

0x07, 0x07,0x07,0x06,0x06,0x05,0x05,0x05,0x04,0x04,0x04,0x04,

0x03, 0x03,0x03,0x03,0x03,0x02,0x02,0x02,0x02,0x02,0x02,0x02,

0x01, 0x01,0x01,0x01};

Command with Low frequency bits, table for tone 1 (the high nybble will be set to 8):

uint8_t psgLow[] = {

0x86, 0x8d,0x87,0x84,0x84,0x87,0x8d,0x84,0x8e,0x8b,0x89,0x89,

0x8b, 0x8e,0x83,0x8a,0x82,0x8b,0x86,0x82,0x8f,0x8d,0x8c,0x8c,

0x8d, 0x8f,0x81,0x85,0x89,0x8d,0x83,0x89,0x8f,0x86,0x8e,0x86,

0x8e, 0x87,0x80,0x8a,0x84,0x8e,0x89,0x84,0x8f,0x8b,0x87,0x83,

0x8f, 0x8b,0x88,0x85,0x82,0x8f,0x8c,0x8a,0x87,0x85,0x83,0x81,

0x8f, 0x8d,0x8c,0x8a};


Command with Low frequency bits, table for tone 2 (the high nybble will be set to A):

uint8_t psgLow[] = {

0xA6, 0xAd,0xA7,0xA4,0xA4,0xA7,0xAd,0xA4,0xAe,0xAb,0xA9,0xA9,

0xAb, 0xAe,0xA3,0xAa,0xA2,0xAb,0xA6,0xA2,0xAf,0xAd,0xAc,0xAc,

0xAd, 0xAf,0xA1,0xA5,0xA9,0xAd,0xA3,0xA9,0xAf,0xA6,0xAe,0xA6,

0xAe, 0xA7,0xA0,0xAa,0xA4,0xAe,0xA9,0xA4,0xAf,0xAb,0xA7,0xA3,

0xAf, 0xAb,0xA8,0xA5,0xA2,0xAf,0xAc,0xAa,0xA7,0xA5,0xA3,0xA1,

0xAf, 0xAd,0xAc,0xAa};

Command with Low frequency bits, table for tone 3 (the high nybble will be set to C):

uint8_t psgLow[] = {

0xC6, 0xCd,0xC7,0xC4,0xC4,0xC7,0xCd,0xC4,0xCe,0xCb,0xC9,0xC9,

0xCb, 0xCe,0xC3,0xCa,0xC2,0xCb,0xC6,0xC2,0xCf,0xCd,0xCc,0xCc,

0xCd, 0xCf,0xC1,0xC5,0xC9,0xCd,0xC3,0xC9,0xCf,0xC6,0xCe,0xC6,

0xCe, 0xC7,0xC0,0xCa,0xC4,0xCe,0xC9,0xC4,0xCf,0xCb,0xC7,0xC3,

0xCf, 0xCb,0xC8,0xC5,0xC2,0xCf,0xCc,0xCa,0xC7,0xC5,0xC3,0xC1,

0xCf, 0xCd,0xCc,0xCa};